1. 什么是进程,什么是线程
进程是程序在计算机内运行的一个实体,而线程是进程在执行时的一个实体。如果把程序看作是食谱,那么进程运行过程就可以看作是厨师做菜的过程,厨师做菜时使用的工具和食材可以看作是进程占用的系统资源。有时候一道菜并不是由一个厨师完成的,而是由多个厨师合作完成的。这些厨师在合作时,有的东西是共用的比如食材,有些东西是私用的比如工具,这时候这些厨师一起作业的过程可以看作是进程在运行,而每个厨师做自己那份工作的过程可以看作是线程在执行。简单概括,进程是一个实体,拥有系统资源以及指令,线程也是一个实体,拥有少量资源执行指令。
2. 进程的内存结构
进程在内存中使用虚拟地址,虚拟地址指向虚拟内存,虚拟内存每个地址都需要通过页表映射到物理内存的一个地址才可以使用。使用虚拟内存的一个好处是,每个进程都可以独享自己的虚拟内存,彷佛自己一个霸占了所有内存,但实际上这些是通过页表映射在共用一个物理内存。当物理内存不够用时,按照页表置换算法,将一个虚拟地址对应物理地址的页面转移到物理磁盘,然后将该物理地址与其他虚拟地址联系起来注册到页表,转移出去的页面在下次需要用时,可以按照同样的过程从磁盘转移回物理内存。进程在虚拟内存中,从低地址到高地址有这么几个成分:预留段,文本段,数据段,堆,栈,内核空间。其中用户空间包括文本段,数据段,堆,栈。下面逐一讲解各个段的作用。
文本段(.text)
文本段存储程序指令以及字符串常量
数据段(.data和.bss)
数据段用于存储全局变量和静态变量,可以分为两个区,其中.data存储有初始化的全局变量和静态变量,.bss存储没有初始化的全局变量个静态变量。
堆
堆用于存储程序动态申请的空间,可以对应到C++中使用new申请的空间。堆是从低地址往高地址扩展,每当堆的空间不足时,可以调用brk函数或sbrk函数移动堆的边界指针来扩展或缩小堆的空间大小。从堆中申请的空间需要自己手动释放,如果不释放可能会造成内存泄漏。内存泄漏会导致程序在运行过程中占用空间不断增大直到发生错误。进程结束后,系统会自动回收堆占用的空间,这说明堆内部的数据的生存周期是整个进程的运行期间。
栈
栈用于存储函数的参数,局部变量和返回值。发生中断时,栈也可以用于存储寄存器等环境值,保存进程的运行状态。栈是从高地址往低地址扩展,栈的空间大小一般有限制,当超出范围时会报错,当然也是可以通过设置编译器系统参数来修改这个限制值。栈内部的数据的生存周期是函数的运行期间,当函数返回值时,栈内部与该函数有关的数据都会被清除,这个清除过程是由系统自动完成的,所以栈存储的数据无需程序员手动释放。
3. 进程和线程的区别
进程与线程的区别,总结起来有以下几点:
(1) 进程是系统分配和调度资源的独立单位,线程是CPU调度和分派的基本单位。
(2) 进程拥有自己独立的系统资源,线程使用进程的资源,自己拥有一点在执行中必不可少的资源(如程序计数器,一组寄存器和栈)。
(3) 一个进程必须有至少一个线程,一个线程只能隶属于一个进程。
(4) 进程之间的资源是独立的,一个进程崩溃并不会影响其他进程。线程之间的资源大多是共享的,一个线程崩溃是有可能导致整个进程崩溃从而导致所有线程崩溃。所以,多进程的程序比多线程的程序更健壮。
(5) 多进程之间的切换比多线程之间的切换耗费资源较大,效率差一些。
(6) 多进程不共享空间资源,需要使用进程间通信技术维持共同工作。多线程共享空间资源,在互相通信时效率更高。
(7) 进程作为独立的实体,可以迁移;线程依附于进程,无法单独迁移。
(8) 总而言之,多线程执行高效,开销小;多进程有利于资源的管理和保护,使得程序更健壮。