进程等待的作用:
- 父进程在它的执行代码中调用进程等待的方法,等待子进程退出,防止子进程变成僵尸进程;
- 也就是说,进程等待是父进程调用某个接口进行等待,父进程等待子进程退出回收子进程的资源,防止子进程变成僵尸进程
进程等待的方法(如何让父进程进行进程等待):wait函数和waitpid函数
wait是waitpid的子功能
等待任意一个退出的子进程
返回值>0等待成功,返回的值是子进程pid
返回值<0等待失败
status是输出型参数,从函数内部拿出特定的数据(是一个整数),waitpid是系统函数,所以是从OS中拿出
子进程退出执行了return或者exit后,会将退出信息写入进程控制块
父进程通过wait函数,wait函数在OS中找到子进程PCB中的退出信息
子进程没退出,父进程就一直等
wait函数:
pid_t wait (int* status)
- 参数是一个指针类型,但是该指针类型并不是要传递一个指针参数,而是一个输出型参数;
- 将wait函数内部计算的某个结果通过status变量返回给调用者;
- 之前所接触的输出型参数有:swap函数中的参数,交换两数的值函数进行传址,将swap的结果传到外部
在编码时有一个代码规范:
如果是输入型,参数定义成引用;
如果是输出或者输入输出参数,参数定义成指针;
wait函数的四个特性:
1.输出型参数,与其对应的有:
- 输入型参数→调用者给被调用函数传参;
- 输入输出型参数;
2.int* status是一个指针类型占四个字节,但是实际中只使用到后两个字节,将这两个字节分为三部分:
- 退出码 + coredump标志位 + 退出信号
- 高位字节表示退出码;
- 低位的一个字节又被分为两个部分,
第一个比特位表示coredump标志位
后七个比特位表示退出信号
退出码:程序正常退出时用到
- main函数的return返回时,return后的数字就是退出码
- exit和_exit中的参数就是退出码
coredump标志位,退出信号是程序异常退出时用到:
- 为0表示该进程在退出时没有内存镜像文件的产生(也就是说没有coredump产生)
- 为1则有镜像文件产生,当前程序退出异常
-
退出信号:进程异常退出时收到的几号信号
- 进程正常退出时coredump标志位和退出信号被置为全0;
- 退出码则是进程正常退出给的数值;
- 异常退出coredump标志位和退出信号会有相应的数;
- 也就是说程序正常退出只有退出码起作用,异常退出则要看标志位和退出信号
异常退出是因为收到了特定的信号,通过status右移7位得到
发信号的过程在信号部分讲解
用退出信号判断进程是否正常退出:
产生coredump文件不能判断进程是否正常退出的原因:
- 程序在运行时崩溃(例如解引用空指针),该进程会收到崩溃信号;
- 该信号可能会让进程产生一个coredump文件,这个文件并不是收到崩溃信号就一定会产生。
- 是否产生coredump文件取决于:
磁盘硬限制,空间大小不足;
系统的软限制,进程可以产生coredump文件但是系统不让其产生
1.判断是否有退出信号
- 正常退出时,退出信号7位全=0;异常退出,退出信号>0;
- 所以
int _status=wait(&status);
status & 0xfff ffff;
,等于0正常退出,>0异常退出
2.判断coredump标志位
3.判断退出码
-
(ststus >> 8) & 0xff;
也就是按位与上八个一
- 或者两个宏
使用wait函数阻止子进程变成僵尸进程
运行情况:
- 父进程创建子进程后,子进程走else if,父进程走else语句;
- 因为父子进程是抢占式执行,那么可能是父进程先执行wait并退出,子进程变成孤儿进程;
- 也可能是子进程先执行先退出,父进程等待退出
- 实际不管谁先运行,父进程都是正常结束的;
- 因为这里牵扯到阻塞的概念,父进程一旦调用wait函数后,当子进程没有退出前,wait函数是不会返回;
- 父进程调用wait函数,wait函数就压到父进程的栈帧上,子进程没有退出,父进程栈帧是不会销毁的,也就是说wait函数不会出栈返回
退出码&退出信号
进程异常退出就看退出信号,退出码没用
不关心子进程退出码,将status设置成NULL就行
WIFRXITED(status),若为正常终止子进程返回的状态则为真
若上面那个非0,提取子进程退出码
阻塞:
把进程从运行队列中拿出来放到等待队列中,在等待一个条件
这是在等待软件
阻塞概念:当调用结果返回之前,当前的执行流会被挂起,并在得到结果之后返回
- 或者说:调用结果返回前,当前线程会被挂起,并在得到结果之后返回
- 例如:父进程被wait函数阻塞,父进程就会被挂起,必须等到子进程结束后才能返回
- 使用上述代码,只需给父进程处加一个死循环即可;
- 运行后通过pstack+父进程进程号查看父进程栈帧;
父进程一直在wait,并没有返回;
对阻塞和非阻塞理解:
1.子进程一种在运行;
- 阻塞:父进程等待子进程运行完后退出;这就是阻塞
- 非阻塞:父进程不等子进程运行是否运行完毕就退出,这样可能会导致孤儿进程;这就是非阻塞
2.子进程已退出
对于两种非阻塞的情况,父进程都是直接退出,但是两种情况父进程退出后,一种正常一种不正常
waitpid函数
返回值:
等待成功,返回子进程退出码
非阻塞等待,没有等到子进程则返回0,等待失败-1,成功返回子进程退出码
wait函数的实现是调用waitpid函数实现
- 非阻塞模式下waitpid函数返回是没有等待子进程的;
- waitpid函数在非阻塞模式下只调用一次并且也没等待到子进程退出,再不去调用该函数;
- 等到子进程退出后子进程就会变成僵尸进程;
所以在非阻塞模式下需要搭配循环;
使用非阻塞,父进程可以在等待的同时做别的事情