【进程等待】是什么 | 为什么 | 怎么办 | wait&阻塞等待

目录

进程等待是什么?

为什么要进程等待? 

如何进程等待?

wait 

阻塞等待 

父进程等待子进程调用wait/waitpid的情况


进程等待是什么?

  • 进程终止会把进程退出的数据(退出码和退出信号)存放到进程的PCB中保存下来,让父进程进行等待。
  • 任何一个子进程在退出时,默认情况下,必须被父进程等待。
  • 任何子进程,在退出的情况下马,一般必须要被父进程进行等待。
  • 3号手册是库函数
  • 2号手册是系统调用函数

  • 若这个子进程的父进程被杀死或父进程不存在,则这个子进程被称为孤儿进程。
  • 孤儿进程会被OS领养,所以严格来讲父进程也存在,父进程就是OS。

  • 子进程在退出的时候,如果父进程不管不顾(父进程不等待)。

  • 子进程退出之后,就处于Z状态(僵尸状态),一直处于Z状态。

  • 很多进程处于Z状态且一直处于Z状态,进程的PCB存在非常大的占空间,非常大的结构体对象,则会造成内存泄露问题。

进程等待的必要性:

  • 之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
  • 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
  • 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

为什么要进程等待? 

 ❓为什么要进程等待

  • 为了获得子进程的退出信息(子进程的任务执行的结果怎么样用户需要知道)
  • 防止内存泄露
  1. 父进程通过等待,解决子进程退出的僵尸问题,回收系统资源(一定要考虑的)
  2. 获取子进程的退出信息,知道子进程是因为什么原因退出的(可选功能)
  • 后面有一些不用等待的场景,后面谈。

如何进程等待?

  • 父进程在子进程运行期间一般都在等待。
  • 父进程在子进程运行的期间也可以做别的事情,一般都让其等待。
  • 父进程等待子进程用系统调用函数
  • wait & waitpid 
  • man 2 wait/waitpid
  • 系统调用函数作用:等待一个子进程的状态发生变化
  • #include <sys/types.h>   #include <sys/wait.h>
  • pid_t wait(int *status);

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

  • wait() 和 waitpid() 是 Unix/Linux 系统调用,用于等待一个或多个子进程的终止。成功这些函数返回一个进程pid,表示已终止的子进程的pid。如果出错,则返回-1。
  • 参数后面也会详细讲解和举例。参数暂时设为NULL。

在Unix和Linux编程中,wait(或更常见的waitpid)函数用于使父进程等待一个或多个子进程结束,并获取其结束状态。这些函数的返回值和参数在不同的上下文中有所不同,但我会为你解释它们的基本含义。

waitpid 函数

waitpid函数的原型如下:

#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);

参数:

  • pid:要等待的子进程的ID。如果pid小于-1,则等待任何组ID等于abs(pid)的子进程。如果pid等于-1,则等待任何子进程。如果pid为0,则等待与调用进程同组的任何子进程。如果pid大于0,则等待进程ID等于pid的子进程。
  • status:一个指向整数的指针,该整数将接收关于子进程状态的信息。你可以使用宏(如WIFEXITEDWEXITSTATUSWIFSIGNALEDWTERMSIG等)来检查这个状态值。
  • options:影响函数行为的选项。常见的选项有WNOHANG(如果子进程没有结束,则立即返回),WUNTRACED(如果子进程因信号而停止,则也返回),以及WCONTINUED(如果子进程因SIGCONT信号而恢复,则也返回)。

返回值:

  • 如果成功,waitpid返回等待的子进程的PID。
  • 如果设置了WNOHANG并且没有子进程可用,则返回0。
  • 如果发生错误,则返回-1,并设置errno以指示错误。

wait 函数

wait函数是waitpid函数的一个特例,它仅等待任何子进程结束,而不提供额外的选项。

其原型如下:

#include <sys/wait.h>
pid_t wait(int *status);

这个函数的参数和返回值与waitpid类似,但没有pidoptions参数。

总的来说,waitwaitpid函数用于在父进程中同步子进程的结束,并获取其状态信息。这在需要处理子进程输出、检查其退出状态或确保子进程正确结束的场景中非常有用。

 

wait 

  • 请看下面代码☞☞
  • 父进程等待子进程耗费的时间❓
  • fork之后创建子进程,父子进程是同时执行各自的任务吗❓
  • main函数没有return或者exit也会终止吗❓
  • 子进程运行5ms退出,进程终止了
  • 父进程在子进程运行5ms是也sleep5ms,子进程终止之后,继续sleep5ms
  • 这里就存在一个5ms的窗口时间:子进程已经终止了,父进程还在休眠,短暂的看到子进处于僵尸状态,Z状态。
  • 当父进程结束休眠,等待子进程,可以看到子进程的僵尸状态消失了且PCB被回收了。
  • 以上就证明了等待是会解决子进程的僵尸问题的
  • 等待的本质就是:获取子进程退出信息(退出码&退出信号)并让OS把PCB释放掉(就是把子进程的Z状态☞X状态)

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

pid_t wait(int*status);
返回值:

  • 成功返回被等待进程pid,失败返回-1。

参数:

  • 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL。
 myprocess.c  
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<string.h>
  4 #include<stdlib.h>
  5 #include<sys/types.h>
  6 #include<sys/wait.h>
  7 
  8 void ChildRun()
  9 {
 10   int cnt = 5;
 11   while(cnt--)
 12   {
 13     printf("I am child,pid: %d,ppid: %d,cnt: %d\n",getpid(),getppid(),cnt);
 14     sleep(1);                                                                                      
 15   }
 16 }
 17 
 18 int main()
 19 {
 20   printf("I am father,pid: %d,ppid: %d\n",getpid(),getppid());//父进程
 21   pid_t id = fork();
 22   if(id == 0)//child子进程
 23   {
 24     //子进程循环运行
 25     ChildRun();
 26     printf("Child quit...\n");
 27     exit(0);//终止进程,子进程直接僵尸
 28   }
 29   //子5s
 30   //父10s
 31   //father父进程,父进程在子进程运行期间先休眠10s再等待子进程....                                  
 32   sleep(10);
 33   pid_t rid = wait(NULL);
 34   if(rid > 0)
 35   {
 36     printf("wait success,rid: %d\n",rid);
 37   }
 38   sleep(3);
 39   printf("father quit ... \n");
 40 }

 

阻塞等待 

  • 我们修改以下代码,请看☞☞ 
  • 父进程在子进程运行期间5ms,一直在等待,没有做任何其他的事情。
  • 只有当子进程终止时(状态变化S☞☞Z时),父进程通过系统调用接口wait等待子进程,拿到子进程的PCB中的退出信息,告诉OS释放掉PCB。
  • 子进程没有终止之前,父进程一直处于阻塞等待中........
  • 子进程本身就是软件,父进程的本质时在等待某种软件条件就绪......(子进程终止,状态从S☞☞Z)
  • 前面我们也讲过阻塞状态,是某个进程等待硬件资源就绪,等待被调度,此进程的PCB处于该硬件资源的等待队列中,该进程处于阻塞状态。(类似理解)
  • 一个进程等待某种资源就绪
  1. 硬件资源就绪
  2. 软件条件就绪

如何理解父进程阻塞等待子进程条件就绪呢❓

  • 阻塞等待子进程
  • 父进程不在被调度,父进程的状态设为S状态。(休眠状态)
  • 父进程的PCB被链入到子进程的等待队列中。
  • 一旦子进程终止.....
  • OS就把等待队列中的父进程唤醒,父进程的状态设为R状态(运行状态)。
  • 继续向后执行。
  • 注意❗无论是硬件还是软件在操作系统OS中都是数据结构的对象,一切皆对象。(这个在后面C++中也会讲到)
myprocess.c
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<string.h>
  4 #include<stdlib.h>
  5 #include<sys/types.h>
  6 #include<sys/wait.h>
  7 
  8 void ChildRun()
  9 {
 10   int cnt = 5;
 11   while(cnt--)
 12   {
 13     printf("I am child,pid: %d,ppid: %d,cnt: %d\n",getpid(),getppid(),cnt);
 14     sleep(1);
 15   }
 16 }
 17 
 18 int main()
 19 {
 20   printf("I am father,pid: %d,ppid: %d\n",getpid(),getppid());//父进程
 21   pid_t id = fork();
 22   if(id == 0)//child子进程
 23   {
 24     //子进程循环运行
 25     ChildRun();
 26     printf("Child quit...\n");
 27     exit(0);//终止进程,子进程直接僵尸
 28   }        
 29   //father
 30   //父进程,父进程在子进程运行期间5ms一直在等待....
 31   pid_t rid = wait(NULL);
 32   if(rid > 0)                                                                                      
 33   {
 34     printf("wait success,rid: %d\n",rid);
 35   }
 36   sleep(3);
 37   printf("father quit ... \n");
 38 }
                             

父进程等待子进程调用wait/waitpid的情况

  • 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
  • 如果不存在该子进程,则立即出错返回。

🙂感谢大家的阅读,若有错误和不足,欢迎指正。下篇讲解重点waitpid。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

唐唐思

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

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

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

打赏作者

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

抵扣说明:

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

余额充值