进程和线程(待续)
一、定义
教科书式回答,进程是资源分配的最小单位,而线程是CPU调度的最小单位。
进程有自己的独立地址空间(虚拟的,4G大小),一个进程崩溃后不会对其他进程产生影响。同一进程内的某一个线程崩溃后,整个进程也崩溃了。因为线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间。
多进程比多线程程序要健壮,但是由于切换时更耗费资源和时间,所以有高并发需求时,一般采用多线程。
想要分清进程和线程的区别,我们先来看一下进程的独立地址空间中包含哪些内容。
进程的虚拟独立地址空间主要包含以下内容:
Text Segment、Data Segment()、BBS(未初始化数据区)、stack、heap、内核空间
TextSegment 表示程序的代码段;
DataSegment (全局初始化数据区/静态数据区,或简称数据段)表示 已经初始化且初值非0的全局变量和静态局部变量 ;
BBS(未初始化数据区-特指全局变量和静态局部变量)表示未初始化或初始值为0的全局变量和静态局部变量;操作系统负责这些段的加载并分配内存空间,这些段在编译期就分段完成。在程序执行前初始化为0或者NULL指针。
栈(stack)用于存储局部变量、函数参数、函数返回值
堆(heap)用于存储程序动态申请的内存,需要程序员手动释放。因为空闲空间的内存是不连续的,所以堆在操作系统中是用链表来存储的。
内核空间(kernel space)属于操作系统的一部分,常驻内存。
1 int a = 0; 全局初始化区 2 char *p1; 全局未初始化区 3 4 void main() 5 { 6 int b; //栈 7 char s[] = "abc"; //栈 s和"abc"都是栈上 8 char *p2; //栈 9 char *p3 = "123456"; //123456\0在常量区,p3在栈上。 10 static int c =0; //全局(静态)初始化区 11 p1 = (char *)malloc(10); //堆 12 p2 = (char *)malloc(20); //堆 13 }
这下知道每个进程空间存放那些东西了吧。我们接着看多进程。
进程上下文
进程上下文:一个进程在执行的时候,CPU的所有寄存器中的值、进程的状态以及堆栈上的内容。内核在进行进程的切换时,需要保存当前进程的所有状态,即保存当前进程的上下文,以便再次执行该进程时,能够恢复切换时的状态,继续执行。
一个进程只会有一个父进程,所以任意一个子进程都可以通过调用getppid()函数获取其父进程的进程ID.
多进程
fork()函数调用成功后,将为子进程申请PCB和用户内存空间.子进程是父进程的副本.在用户空间将复制父进程用户空间所有数据(代码段、数据段、BBS、堆、栈),复制父进程内核空间PCB中的绝大多数信息.子进程从父进程继承下例属性:有效用户、组号、进程组号、环境变量、对文件的执行时关闭标志、信号处理方式设置、信号屏蔽集合、当前工作目录、根目录、文件模式掩码、文件大小限制和打开的文件描述符(特别注意:共用同一文件表项)。我可以认为父子进程都几乎是相同的,因为他们拥有相同的系统资源(除了各自的PID不一样)吗?
多线程
线程不再独有虚拟地址空间,而是和同一进程内的其他线程共享地址空间。但是线程各自拥有独立的寄存器(不然还用volatile干嘛)和堆栈(比如各线程的局部变量)。
多线程 vs 多进程
- 多线程之间堆内存共享,而进程相互独立,线程间通信可以直接基于共享内存来实现,比进程的常用的那些多进程通信方式更轻量。
- 在上下文切换来说,不管是多线程还是都进程都涉及到寄存器、栈的保存,但是线程不需要切换 页面映射(虚拟内存空间)、文件描述符等,所以线程的上下文切换也比多进程轻量
- 多进程比多线程更安全,一个进程基本上不会影响另外一个进程
在实际的开发中,一般不同任务间(可以把一个线程、进程叫做一个任务)需要通信,使用多线程的场景比多进程多。但是多进程有更高的容错性,一个进程的crash不会导致整个系统的崩溃,在任务安全性较高的情况下,采用多进程。