大家好,我是苏貝,本篇博客带大家了解Linux进程(9)进程控制2,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
目录
- 一. 为什么要进程等待
- 二. 如何进行进程等待
- 1.wait函数—系统调用
- 2. waitpid函数
- (A).第一个参数:pid_t pid
- (B).第二个参数:int* status
- a. 第二个参数是输出型参数
- b. 这个参数表示子进程的退出信息(包括子进程的退出码和退出信号)
- c. WIFEXITED 和 WEXITSTATUS
- (C).第三个参数:int options
结论:任何子进程,在退出的情况下,一般必须要被父进程进行等待。进程在退出时的状态变为Z(僵尸状态),如果父进程不管不顾,那么子进程的task_struct一直存在而且所占的空间较大,造成内存泄露的问题
一. 为什么要进程等待
- 父进程通过等待,解决子进程退出的僵尸问题,回收系统资源(一定要考虑的)
- 获取子进程的退出信息,知道子进程是因为什么原因退出的(可选的功能)
二. 如何进行进程等待
1.wait函数—系统调用
先来查看一下wait函数,从头文件就可以看出,wait函数是系统调用函数。作用:等待任意一个子进程退出
函数的参数暂时不用管,(我们下面说waitpid中会说到)可以直接传NULL
函数的返回值是父进程等待子进程,等待成功时,子进程的pid
修改.c文件
上面代码的意思是先用fork函数生成一个子进程,子进程会执行ChildRun函数,执行完成后,进程终止。此时子进程是Z状态(僵尸状态)。睡眠8秒后,父进程等待子进程退出,子进程已经退出,wait函数得到子进程的pid,解决子进程退出的僵尸问题。父进程再睡眠5秒后父进程终止
所以wait函数是能解决子进程退出的僵尸问题
父进程在执行wait函数时,如果子进程没有退出,那么父进程一直在进行阻塞等待。阻塞?我们之前了解的阻塞是等待某种硬件资源(如键盘资源)就绪,等待时是将进程的pcb链入键盘资源的等待序列中。那我们如何理解阻塞等待子进程?
阻塞等待:将父进程的状态设为S,再链入子进程的队列中(子进程本身就是软件)。
一旦子进程退出,操作系统调度时发现子进程退出,就将父进程唤醒
2. waitpid函数
先查看waitpid函数
我们看见waitpid函数有3个参数
(A).第一个参数:pid_t pid
第一个参数:等待的子进程的pid。如果pid==-1,表示等待任意一个子进程,与wait等效。pid>0表示等待其进程ID与pid相等的子进程
(B).第二个参数:int* status
a. 第二个参数是输出型参数
什么是输出型参数?我们自己在代码中定义一段内存空间,把空间的地址传进来。操作系统在等待时,在底层把对应的数据通过传的指针带到用户层,让用户看到。
直接举例:
我们在代码中定义变量a,将a的地址通过sacnf传到操作系统,我们键盘输入的数据经过scanf函数读到了a中,最后我们可以通过打印看到a的值
b. 这个参数表示子进程的退出信息(包括子进程的退出码和退出信号)
如果只要子进程的退出码和退出信号的话,能不能直接定义2个全局变量exit_code和exit_signal,然后将退出码和退出信号分别写在exit_code和exit_signal呢?
当然不能,因为进程具有独立性,子进程写入时,会先发生写时拷贝,所以父进程看不到子进程写入的数据,因此不能使用全局变量来接收退出码和退出信号
我们来见一下这个退出信息
修改.c文件
status=256,这是什么意思呢?
status不能简单的当作整形来看待,可以当作位图来看待。status是int类型的,有32个比特位,只研究status低16比特位。其中次8个比特位表示退出状态,即退出码。前7个比特位表示退出信号,第8个比特位暂时不用理解
因此status=256=2^8即0000 0001 0000 0000,所以退出码=1,退出信号=0
我们如何通过代码直接打印出退出码和退出信号呢?
退出码是次8位,那我们先status>>8,再&(按位与)0xFF(即0x1111 1111)
退出信号:status & 0x7F(即0x0111 1111)
修改.c文件
退出码是exit函数的参数:123
因为进程没有异常,所以退出信号=0
让子进程死循环,在命令行中使用kill -9信号杀掉子进程
c. WIFEXITED 和 WEXITSTATUS
上面是通过将status右移或按位与得到子进程的退出码和退出信号,那有没有什么东西能够直接告诉我们退出码和退出信号呢?
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(若退出信号为0,则为真;反之则为假)
WEXITSTATUS(status): 若WIFEXITED非零(退出信号为0),提取子进程退出码。(查看进程的退出码)
修改.c文件
上面代码的意思是,如果子进程代码跑完了,即正常退出,那么查看子进程的退出码。如果子进程不正常退出,提示一下。我们之前写的status右移和按位与没删除是想看看2个框框内的结果是否一致
结果是一致的。
我们再来看看进程不正常退出的情况,之前的.c文件只修改ChildRun函数,让里面有野指针
结果也是正确的,因为有野指针,所以是段错误,退出信号为11
(C).第三个参数:int options
我们上面讲的,都是如果子进程没有退出,那么父进程在执行waitpid时在阻塞等待,这表示在等待期间,父进程其它事情什么都没有干。那如果我们想让父进程做一些其它的事情呢?这就需要用到waitpid函数的第三个参数:int options
当第三个参数为0时,表示父进程要阻塞等待。为WNOHANG时,表示非阻塞等待:每隔一段时间就会查看子进程是否退出,如果没有退出,那就可以做其它事情。因为每隔一段时间就要查看子进程是否退出,所以非阻塞等待要配合循环一起使用,这就叫非阻塞轮询
讨论一下waitpid函数的返回值rid
- rid>0:等待成功,子进程退出,父进程回收成功
- rid<0:等待失败,可能是第一个参数有问题,等待的子进程a不是父进程b的子进程
- rid==0:检测是成功的(子进程a是父进程b的子进程),但是进程还没有退出,需要下一次进行重复等待
先来试试非阻塞等待,父进程暂时不做其它事情
好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️