进程等待

进程等待的必要性

  1. 子进程退出,父进程如果不管不顾,就会造成“僵尸进程”,从而造成内存泄漏等问题。
  2. 进程一旦进入僵尸进程状态,就刀枪不入,kill  -9   也无法杀死。
  3. 父进程给子进程派出的任务完成情况,不进行进程等待就无法获取。如:子进程运行完成,结果是否正确;程序是否正常退出。
  4. 父进程通过进程等待的方式,回收子进程资源,获取子进程的退出信息。

进程等待方法

wait

1、函数使用

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

pid_t wait (int * status);

2、返回值:

wait成功:返回被等待进程的 pid

wait失败:返回 -1

3、参数:

输出型参数,获取子进程的退出状态,不关心返回值则可以设置成NULL

4、wait 是阻塞式的函数,子进程结束才返回。

5、wait 的个数必须和子进程个数一致,否则如果wait次数少于子进程数,没有被等待的子进程就会进入僵尸状态;wait次数大于子进程数,除了调用出错,没别的影响。

6、进程阻塞等待代码实现

#include <sys/wait.h>                                                                                                                                                                                
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
 
int main()
{
     pid_t pid = fork();
     if(pid < 0)
     {
         printf("%s fork error\n",__FUNCTION__);
         return 1;
     }
     else if(pid == 0)
     {
         //child
         printf("child is run, pid is : %d\n",getpid());
         sleep(5);
         exit(257);
     }
     else
     {
         //father
         int status = 0;
         pid_t ret = waitpid(-1,&status,0);//阻塞式等待,等待5秒
         printf("this is test for wait !\n");
         if(WIFEXITED(status) && ret == pid)
             printf("wait child 5s success, child return code is : %d.\n",WEXITSTATUS(status));
         else
         {
             printf("wait child failed ,return.\n");
             return 1;
         }
     }
     return 0;
 }

运行结果:

[LXY@localhost wait]$ ./wait 
//立即执行并打印
child is run, pid is : 20102

//父进程阻塞等待5秒之后,等待到子进程的退出,然后执行并打印结果
this is test for wait !
wait child 5s success, child return code is : 1.

解释:子进程不退出,父进程就一直阻塞在那,等待子进程退出后才执行后面代码。

waitpid

1、函数使用

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

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

2、返回值

  • 正常返回的时候 waitpid 返回收集到的子进程的进程ID;
  • 如果第三个参数设置了选项 WNOHANG,而且调用中 waitpid 发现没有已经退出的子进程可以收集,则返回 0 ;
  • 如果调用中出错,则返回 -1 ,这时errno 会被设置成相应的值来指示错误所在。

3、参数

(1)pid

         pid = -1,等待任意一个子进程。与wait等效。

         pid > 0,   这时pid是一个子进程的进程ID,等待其进程ID与pid相等的子进程。

(2)status

         WIFEXITED(status):若为正常终止子进程返回的状态,则为真。(查看进程是否正常退出)

         WEXITSTATUS(status):若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

(3)options

          WNOHANG:若pid指定的进程没有结束,则waitpid()函数返回0,不予以等待;若正常结束,则返回该子进程的ID。

4、waitpid是非阻塞轮询式的等待

(1)优点:能够更灵活的控制代码,充分利用等待时间做其他事情。

(2)缺点:代码更复杂。

5、进程非阻塞等待代码实现:

#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
     pid_t pid = fork();
     if(pid < 0)
     {
           printf("%s fork error\n",__FUNCTION__);
           return 1;
     }
     else if(pid == 0)
     {
         //child
         printf("child is run, pid is : %d\n",getpid());
         sleep(5);
         exit(1);                                                                                                                                                                                     
     }
     else
     {
         //father
         int status = 0;
         pid_t ret = 0;
         do{
              ret = waitpid(-1,&status,WNOHANG);//非阻塞式等待
              if(ret == 0)
                   printf("child is running !\n");
              sleep(1);
         }while(ret == 0);
         if(WIFEXITED(status) && ret == pid)
              printf("wait child 5s success, child return code is : %d.\n",WEXITSTATUS(status));
         else
         {
              printf("wait child failed ,return.\n");
              return 1;
         }
     }
     return 0;
 }

运行结果:

[LXY@localhost wait]$ ./waitpid 
//立即执行并打印
child is running !
child is run, pid is : 20704

//每隔一秒执行一次
child is running !
child is running !
child is running !
child is running !

//五秒等待结束,打印最终结果
wait child 5s success, child return code is : 1.

解释:这个代码完美解释了waitpid的轮询式非阻塞等待进程过程。就相当于它每隔一秒来查看一次子进程的退出情况,若查看时子进程退出了,就返回结果,若还没退出就去做其他事,过一秒再来查看。(这个查看间隔时间是自己设置的)

进程等待过程

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

获取子进程的status

  • wait/waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果status设置成NULL,则表示不关心子进程的退出状态信息。
  • 如果不是NULL,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status不能当作整型来看待,可以作为位图来研究(下图研究了status低16位比特位)

如图:

如何通过代码获取子进程的status?

#include <sys/wait.h>                                                                                                                                                                                
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

int main()
{
   pid_t pid;

   if((pid = fork()) == -1)
   perror("fork"),exit(1);

   if(pid == 0)
   {
       sleep(20);
       exit(10);
   }
   else
   {
       int st;
       int ret = wait(&st);
 
       if(ret > 0 && (st & 0X7F) == 0)
       {
           //正常退出
           printf("child exit code: %d\n",(st >> 8) & 0XFF);
       }
       else if(ret > 0)
       {
           //异常退出
            printf("sig code : %d\n",st & 0X7F);
       }
   }
 }

运行结果:

//正常退出结果
[LXY@localhost wait]$ ./status 
child exit code: 10

//新开一个终端,kill指令杀死子进程结果
[LXY@localhost wait]$ ./status 
sig code : 15

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值