进程控制:进程等待(回收子进程)


进程等待的必要

当一个进程终止的时候,它的资源,比如说PCB,数据等不会被立马清理掉。它会保持在已经终止的状态,这种状态称为“僵尸状态”,直到被父进程确认。父进程wait,即父进程向内核确认子进程已经终止,可以为子进程“收尸”了,内核会把子进程的退出信息传给父进程,然后清理掉子进程的资源,这个时候子进程才算真正地终止了!

总结:

  • 父进程等待,可以获取子进程的退出信息,知道子进程的执行结果。
  • 父进程等待,可以释放子进程的资源,让子进程真正地退出,避免一直消耗系统的存储资源,造成“内存泄露”等危害。
  • 父进程等待,可以保证时序的问题,子进程先于父进程退出,避免让子进程变为孤儿进程。

进程等待的方法

wait函数

一个进程可以通过调用wait函数等待子进程。wait函数是系统调用函数。

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);

返回值:返回被等待进程的pid,如果等待失败,返回-1。
参数:输出型参数,可以获取子进程的退出状态,如果不需要获取子进程的退出状态,则设置为NULL。
测试:

  1 #include <stdio.h>                                                     
  2 #include <unistd.h>
  3 #include <sys/types.h>
  4 #include <sys/wait.h>
  5 #include <stdlib.h>
  6
  7 int main(void)
  8 {
  9   pid_t id = fork(); //创建子进程
 10   if(id == 0)
 11   {
 12     //child
 13     //执行5秒
 14     int cnt = 5;
 15     while(cnt)
 16     {
 17       printf("child[%d] , cnt:%d\n", getpid(), cnt);
 18       sleep(1);
 19       cnt--;
 20     }
 21     exit(EXIT_SUCCESS);
 22   }
 23   sleep(10);
 24   pid_t ret = wait(NULL);
 25   if(ret > 0)
 26   {
 27     //wait success, ret is pid;
 28     printf("father wait child[%d] success\n", ret);
 29   }
 30   else{                                                                
 31     //wait failed.
 32     printf("father wait failed\n");
 33   }
 34   return 0;
 35 }

现象:子进程执行5秒后,终止了,但是内核没有立马清理掉它的资源,所以此时是僵尸状态,再过了5秒之后,父进程休眠完毕,然后等待子进程,确认子进程已经终止,返回子进程的pid,然后内核开始清理子进程资源,子进程真正地终止了,又过了5秒后父进程也终止了。
在这里插入图片描述

通过wait函数的输出型参数可以获得子进程的退出信息。

waitpid函数

waitpid函数也可以使得父进程等待子进程

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);

先不关心第二个和第三个参数,第二个参数可以设置为NULL,第三个参数暂时设置为0。

第一个参数:
1、如果第一个参数pid传的是某个具体的进程的进程ID,表示等待该指定进程
2、如果第一个参数pid传的是-1,表示等待父进程的任意子进程。

第三个参数:

  • 传的是0,表示父进程是挂起等待子进程的(阻塞等待)。可以理解为“父进程在等待子进程的过程中,什么事情也没做,在干等”。
  • 传的是宏WNOHANG,表示父进程是非阻塞等待。若等待的子进程还没有终止,那么waitpid函数立即返回0,不予以等待。若等待的子进程已经正常结束,那么waitpid函数返回等待子进程的PID

wait(&status) 等价于 waitpid(-1, &status, 0)

【注意事项】

  • 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,获得子进程退出信息,并且释放被等待子进程资源。
  • 如果在任意时刻调用wait/waitpid,子进程存在且还在正常运行,则父进程可能会发生阻塞。
  • 如果试图等待一个当前不存在的进程,wait/waitpid会调用出错,并立即返回。

获取子进程退出信息

在上述并没有具体解释参数status的作用。

  • 在wait和waitpid函数中,status的作用是一样的,它是输出型参数。
  • 如果给status传的是NULL,则表示不需要获取子进程的退出信息。
  • 如果给status传的是非NULL,则可以获取被等待进程的退出信息。

status是一个指向整形的指针。但是一个进程的退出信息那么多,怎么可能会那么简单地用一个整型就知道进程的退出信息了呢?实际上,并不是简单地看待status指向的整形,而是当作位图来看,一个整型有32位,这样就可以全面地描述被等待进程的退出信息了。

只用研究低16个比特位。
进程退出的情况有四种:
1、正常退出(自愿,代码执行完,结果正确)
2、错误退出(自愿,代码执行完,结果不正确)
3、异常退出(非自愿,代码未执行完,退出码无意义)
4、被其他进程终止(非自愿,代码未执行完,退出码无意义)

这四种情况,可以按照进程是否收到信号来分类,第一种和第二种进程未收到信号,第三和第四种进程收到信号。

在这里插入图片描述
当被等待进程不是被信号所终止时,低8位全是0,而次低8位则是被等待进程的退出码。
当被等待进程是被信号所终止时,低7位表示被等待进程收到的信号。

如果进程是正常终止,如何显示地知道退出码?
在这里插入图片描述
如果进程是收到信号而终止,如何知道它收到了什么信号?直接就是低7位表示的是进程收到的信号,如果是非法的信号,说明它没有收到信号,这个值是无效的。

测试:

  1 #include <stdio.h>                                                    
  2 #include <unistd.h>
  3 #include <sys/types.h>
  4 #include <sys/wait.h>
  5 #include <stdlib.h>
  6 
  7 int main(void)
  8 {
  9   pid_t id = fork(); //创建子进程
 10   if(id == 0)
 11   {
 12     //child
 13     //执行5秒
 14     int cnt =7;
 15     while(cnt)
 16     {
 17       printf("child[%d] , cnt:%d\n", getpid(), cnt);
 18       sleep(1);
 19       cnt--;
 20     }
 21     exit(12);
 22   }
 23   sleep(10);
 24   int status;
 25   pid_t ret = waitpid(id, &status, 0);
 26   if(ret > 0)
 27   {
 28     //wait success, ret is pid;
 29     printf("father wait child[%d] success\n", ret);
 30   }
 31   else{
 32     //wait failed.
 33     printf("father wait failed\n");
 34   }
 35   printf("get a exit num : %d\n, get a single:%d", (status >> 8) & 0XF    FFF, status & 0XFFFF);
 36   sleep(2);                                                           
 37   return 0;
 38 }

在这里插入图片描述

当然,还可以不需要进行位运算,系统提供了两个宏,可以得到退出信息

  • WIFEXITED(status):如果被等待进程正常退出则未真。
  • WEXITSTATUS(status):如果WFIEXITED(status)为真,提取被等待进程的退出码。
  1 #include <stdio.h>                                                                                                                                              
  2 #include <unistd.h>
  3 #include <sys/types.h>
  4 #include <sys/wait.h>
  5 #include <stdlib.h>
  6 
  7 int main(void)
  8 {
  9   pid_t id = fork(); //创建子进程
 10   if(id == 0)
 11   {

 12     //child
 13     //执行5秒
 14     printf("i am child precess\n");
 15     sleep(5);
 16     exit(12);
 17   }
 18   sleep(3);
 19   int status;
 20   printf("father begin wait\n");
 21   pid_t ret = waitpid(id, &status, 0);
 22   if(ret > 0)
 23   {
 24     //wait success, ret is pid;
 25     printf("father wait child[%d] success\n", ret);
 26   }
 27   else{
 28     //wait failed.
 29     printf("father wait failed\n");
 30   }
 31   if(WIFEXITED(status))
 32   {
 33     printf("exit code : %d\n", WEXITSTATUS(status));
 34   }
 35   else{
 36     printf("get a signal\n");
 37   }
 38   sleep(2);
 39   return 0;
 40 }   

执行结果
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小酥诶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值