在这篇文章中我们学习了进程创建,进程终止,进程等待三个内容,首先学习使用fork()函数来创建一个子进程和创建子进程之后使用写时拷贝技术解决父子进程数据问题,然后就是进程终止和进程等待,进程终止主要是讲了使用exit()函数来终止一个进程,进程等待主要是为了解决僵尸进程的问题,回收僵尸进程的退出信息

pid_t fork(void);








我们都知道,进程是由内核的进程数据结构和进程的代码和数据组成的,内核的数据结构我们现在所知道的有两个:task_struct+mm_struct,所以此过程操作系统会创建子进程的内核数据结构和页表,然后子进程的代码继承自父进程,数据通过写时拷贝的方式进行共享
写时拷贝是维护进程独立性的一个非常重要的手段,其通常是指在系统识别到有人要更改数据时,会在系统的内存中为该数据再开辟一个空间,然后将该数据的值拷贝过去,最后在新空间上进行修改,从而实现两个进程的数据的分离,两个进程对该数据的修改互不影响,从而维护了进程的独立性


写时拷贝是一种延迟拷贝的策略,只有真正使用的时候才进行拷贝分离,暂时不使用但是想要的空间不会进行写时拷贝,此时该空间可以先分配给其他进程进行使用,这样就变向提高了内存的利用率
在我们的C语言或者C++语言中,我们通常会写main函数,这个main函数我们知道是程序的入口,通常我们会在最后写上return 0;

那么这个return 0;中的0到底是什么意思?
其实,这个0代表的是进程的退出码,0表示的是进程正常结束,运行结果正确非零表示进程运行结束,运行结果不正确,其中退出码的非零就是代表结果运行不正确的原因,退出码是返回给当前进程的父进程进行接收的
echo $?命令
echo $?查看退出码




exit()函数exit()函数都可以使程序马上停下来







通过上图我们会发现,调用_exit()函数时,只会直接终止程序,不会做任何事情,调用exit()时,会执行用户定义的清理函数和刷新缓冲区和关闭对应的流,因此,一般情况下,我们常用的是exit()函数
我们知道,一个进程是由自己的内核数据结构和进程代码和数据组成的,当一个进程退出的时候,首先自己的进程状态会变成Z状态,也就是所谓的僵尸状态,此时需要等其父进程对其进程回收,由终止我们知道其需要给其父进程返回退出码,这个进程的状态马上变成X状态,其父进程的相关代码会回收(释放)其数据和相关代码,因为当该进程终止的时候,其数据和代码已经没有任何意义了,但是需要注意的是管理这个进程的内核数据结构可能不会被释放,这个内核数据结构在系统内存充足的时候会被加入一个链表,这个链表叫做废弃数据结构链表,专门收集终止的进程的内核数据结构,也叫数据结构缓冲池,或者slap分派器


int* status;当我们的参数传成NULL的时候,那么这个调用这个函数的进程就可以等待任何进程了,wait函数的返回值是等待的进程的pid,如果等待成功,那么会返回等待进程的Pid,如果等待失败,则会返回-1wait函数来等待子进程,那么当子进程被杀死的时候,子进程的状态马上就会变成僵尸状态,那么此时父进程就会回收子进程的退出信息,回收之后,子进程就会消失,父进程正常执行其后面的代码,直至退出
使用man手册查看使用方法

返回值:如果返回值的结果大于0,则说明等待成功,返回值为等待的进程的pid,如果返回值的结果小于0,则说明等待失败
参数pid_t pid:等待的进程的pid
参数int status*:这个参数是一个输出型参数,可以将系统中的一些数据带出来,通常包含等待进程的退出码或者是异常信号
参数int option:现在先写0,表示阻塞等待,现在先不考虑非阻塞等待
waitpid()函数的使用



注意:关于子进程的退出码和异常信号现在先记住写法:退出码就是(status>>8)&0xFF,异常信号是status&0x7F
在这篇文章中首先我们学习了使用fork()函数来创建一个子进程和写时拷贝技术解决父子进程数据问题,然后就是进程终止和进程等待,进程终止主要是讲了使用exit()函数来终止一个进程,进程等待主要是为了解决僵尸进程的问题,回收僵尸进程的退出信息