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

pid_t fork(void);创建fork.c和makefile文件



实验结果
多次实验现象




多次执行可执行程序之后,我们发现,几乎全部都是父进程先执行代码,然后才是子进程执行代码
构建项目



我们只使用一个printf语句,分别获取pid和ppid,查看对应pid和ppid之间的关系
实验结果

显然就是两个进程去调用printf函数,从而出现两条结果


结论:通过上面的实验结果可以看出,一定是有两个进程执行了printf函数,因此打印出两个结果,并且这两个结果的pid和ppid之间会存在父子关系,第一个执行printf函数的进程是第二个执行printf函数的父进程(通过pid和ppid关系中可以看出来)



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


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

那么这个return 0;中的0到底是什么意思?
其实,这个0代表的是进程的退出码,0表示的是进程正常结束,运行结果正确,非零表示进程运行结束,运行结果不正确,其中退出码的非零就是代表结果运行不正确的原因,退出码是返回给当前进程的父进程进行接收的,父进程需要知道自己子进程退出或者终止的原因,从而回收子进程的资源,我们平时在写代码的时候是可以自己设置退出码的,但是一般我们设置退出码的标准还是按照系统给出的对应的退出码的意义来进行设置,稍微会介绍查看1-100的退出码意义
echo $?命令
echo $?查看退出码





如果我们在main函数中不写return 0;
源代码

实验结果

当我们查看130的错误信息

在程序中的任何地方使用exit()函数
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,0xFF:F表示的是十六进制15,二进制表示为1111,故0xFF表示的是11111111,完整表示为:0x00000000 00000000 00000000 11111111,(status>>8)&0xFF表示的是取status中的第二个八位数,即从第9到第16位的数字,这部分的数字代表退出进程的退出码

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