ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【Linux】进程控制2——进程等待(wait&&waitpid) [打印本页]

作者: 水军大提督    时间: 2024-7-15 09:10
标题: 【Linux】进程控制2——进程等待(wait&&waitpid)
1. 进程等待的必要性——回收子进程

父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
   什么是进程等待? 
  通过系统调用wait/waitpid,来举行对子进程举行状态检测与回收功能。 
我们先来创建一个僵尸进程
  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. int main()
  5. {
  6.     pid_t id=fork();
  7.     if(id<0)
  8.     {
  9.         perror("fork\n");
  10.         return 1;
  11.     }
  12.     else if(id==0)
  13.     {
  14.     //child
  15.     int cnt=5;
  16.     while(cnt)
  17.         {
  18.             printf("I am child ,pid:%d,ppid :%d",getpid(),getppid());
  19.             cnt--;
  20.             sleep(1);
  21.         }
  22.         exit(0);//子进程在这里退出
  23.     }
  24.     else
  25.     {
  26.     //parent
  27.       while(1)
  28.         {
  29.             printf("I am father,pid:%d,ppid :%d",getpid(),getppid());
  30.             sleep(1);
  31.         }
  32.     }
  33. }
复制代码
监督脚本 
  1. while :; do ps ajx | head -1 && ps ajx | grep mytest | grep -v grep; sleep 1; echo "______________";done
复制代码
 

   这个父进程的父进程19897是谁啊?
  

  bash进程啊
   我们看到它这个子进程退出之后,父进程还没有退出,子进程不绝处于僵尸进程 
2 .wait系统调用接口

在Linux中,wait函数是一个系统调用用于等待子进程的停止并获取其停止状态。该函数的原型如下所示:
  1. #include<sys/types.h>
  2. #include<sys/wait.h>
  3. pid_t wait(int*status);
复制代码
wait函数返回值(PID)有以下几种可能的取值:

参数:(我们这里先不讲)

           函数功能是:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当进步程的某个子进程已经退出,
    我们来看看两种情况来理解这个函数 
2.1.子进程等待父进程回收

  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<sys/types.h>
  5. #include<sys/wait.h>
  6. int main()
  7. {
  8.     pid_t id=fork();
  9.     if(id<0)
  10.     {
  11.         perror("fork\n");
  12.         return 1;
  13.     }
  14.     else if(id==0)
  15.     {
  16.     //child
  17.     int cnt=5;
  18.     while(cnt)
  19.         {
  20.             printf("I am child ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  21.             cnt--;
  22.             sleep(1);
  23.         }
  24.         exit(0);//子进程在这里退出
  25.     }
  26.     else
  27.     {
  28.     //parent
  29.       int cnt=10;
  30.       while(cnt)
  31.         {
  32.             printf("I am father ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  33.             cnt--;
  34.             sleep(1);
  35.         }
  36.         //目前进程等待是必要的——回收僵尸进程
  37.         pid_t ret = wait(NULL);//我们先这么用着
  38.         if(ret == id)//id是父进程fork的返回值——子进程的PID
  39.         {
  40.             printf("wait success, ret : %d\n",ret);
  41.         }
  42.         sleep(5);
  43.     }
  44. }
复制代码
   当子进程停止后,wait函数会返回子进程的进程ID(PID) 
  我们分析一下程序:  前5秒的时间父子进程都在打印,5秒之后子进程先退,父进程 没有举行wait,还在打印,10秒之后父进程回收子进程,回收完后父进程还要继续等待5秒
我们看看运行结果

 我们看看隔壁的监控情况 


很符合我们的预期啊!!!
   wait一次是只能等待任意一个进程退出的,只不过我们这里只有一个子进程,以是只回收了这个子进程
   我们来看看怎么回收多个进程的
  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<sys/types.h>
  5. #include<sys/wait.h>
  6. #define N 10
  7. void RunChild()
  8. {
  9.     int cnt =5;
  10.     while(cnt)
  11.     {   
  12.         printf("I am child,pid:%d,ppid:%d\n",getpid(),getppid());
  13.        cnt--;
  14.         sleep(1);
  15.     }
  16. }
  17.         
  18. int main()
  19. {
  20.     int i;
  21.     for(i=0;i<N;++i)
  22.     {
  23.     pid_t id =fork();
  24.     if(id==0)
  25.         {
  26.      RunChild();
  27.         exit(0);//每个子进程走到这里就退出了
  28.         }
  29.     printf("creat child :%d  sucess\n",getpid());//这句话只有父进程会执行
  30.     }
  31.    
  32.     sleep(10);
  33.    
  34.     //等待,我们这里有10个子进程
  35.     for(i=0;i<N;++i)
  36.     {
  37.     pid_t ret =wait(NULL);
  38.     if(ret > 0)
  39.         {
  40.         printf("wait %d success\n",ret);
  41.         }
  42.     }
  43.     sleep(5);//父进程最后等一下
  44. }
复制代码
我们看看实行情况 
 

再看看监督情况
  1. ​while :; do ps ajx | head -1 && ps ajx | grep mytest | grep -v grep; sleep 1; echo "______________";done
复制代码
5个这个
 5个这个
 最后变这个

10个进程又怎么样呢?全给你回收了
2.1.2.父进程等待回收子进程

我们上面都是子进程等待父进程来回收,我们现在让父进程来等待回收子进程
   我们上面的例子都是子进程会退出,然后被wait函数回收,那么子进程要是不退出呢?
  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<sys/types.h>
  5. #include<sys/wait.h>
  6. #define N 10
  7. void RunChild()
  8. {
  9.     while(1)//子进程永远不死
  10.     {   
  11.         printf("I am child,pid:%d,ppid:%d\n",getpid(),getppid());
  12.         sleep(1);
  13.     }
  14. }
  15.         
  16. int main()
  17. {
  18.     int i;
  19.     for(i=0;i<N;++i)
  20.     {
  21.     pid_t id =fork();
  22.     if(id==0)
  23.         {
  24.      RunChild();
  25.         exit(0);//每个子进程走到这里就退出了
  26.         }
  27.     printf("creat child :%d  sucess\n",getpid());//这句话只有父进程会执行
  28.     }
  29.    
  30.     sleep(10);
  31.    
  32.     //等待,我们这里有10个子进程
  33.     for(i=0;i<N;++i)
  34.     {
  35.     pid_t ret =wait(NULL);
  36.     if(ret > 0)
  37.         {
  38.         printf("wait %d success\n",ret);
  39.         }
  40.     }
  41.     sleep(5);//父进程最后等一下
  42. }
复制代码
运行结果
 

 监督窗台,不绝都是下面这个

事实表明子进程不退的话,父进程就会在wait函数那边阻塞等待
    阻塞等待:如果子进程不退出,父进程默认在wait这个系统调用的地方不返回,这种状态叫阻塞状态
  综上, 我们可以得出

   来总结一番
    当父进程忘了用wait()函数等待已停止的子进程时,子进程就会进入一种无父进程的状态,此时子进程就是僵尸进程.
  
    wait()要与fork()配套出现,如果在使用fork()之前调用wait(),wait()的返回值则为-1,正常情况下wait()的返回值为子进程的PID.
  
    如果先停止父进程,子进程将继续正常举行,只是它将由init进程(PID为1)继续,当子进程停止时,init进程捕获这个状态.
  
  3. waitpid方法

   waitpid函数是Linux中用于等待指定子进程停止的系统调用。
          与wait函数雷同,waitpid函数也可以用于获取子进程的停止状态。
  1. #include <sys/types.h>
  2. #inlclude <sys/wait.h>
  3. pid_ t waitpid(pid_t pid, int *status, int options);
复制代码
          函数功能是:父进程一旦调用了waitpid就立即阻塞自己,由waitpid自动分析是否当进步程的某个子进程已经退出,
    从本质上讲,系统调用waitpid和wait的作用是完全雷同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为我们编程提供了另一种更灵活的方式。
下面我们就来具体介绍一下这两个参数:
   pid:
  从参数的名字pid和范例pid_t中就可以看出,这里必要的是一个进程ID。但当pid取差别的值时,在这里有差别的意义。
   options:
  options提供了一些额外的选项来控制waitpid,现在在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用,比如:
  1.  ret = waitpid(-1,  NULL,  WNOHANG | WUNTRACED);
复制代码
如果我们不想使用它们,也可以把options设为0,如:
  1.      ret = waitpid(-1,  NULL,  0);
复制代码

看到这里,聪明的读者可能已经看出端倪了:wait不就是经过包装的waitpid吗?
没错,察看<内核源码目录>/include/unistd.h文件349-352行就会发现以下程序段:
  1. static inline pid_t wait(int * wait_stat)
  2. {
  3.     return waitpid(-1,wait_stat,0);
  4. }
复制代码
  返回值和错误
  waitpid的返回值比wait稍微复杂一些,一共有3种情况:
   我们先看个简单的例子
  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<sys/types.h>
  5. #include<sys/wait.h>
  6. int main()
  7. {
  8.     pid_t id=fork();
  9.     if(id<0)
  10.     {
  11.         perror("fork\n");
  12.         return 1;
  13.     }
  14.     else if(id==0)
  15.     {
  16.     //child
  17.     int cnt=5;
  18.     while(cnt)
  19.         {
  20.             printf("I am child ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  21.             cnt--;
  22.             sleep(1);
  23.         }
  24.         exit(0);//子进程在这里退出
  25.     }
  26.     else
  27.     {
  28.     //parent
  29.       int cnt=10;
  30.       while(cnt)
  31.         {
  32.             printf("I am father ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  33.             cnt--;
  34.             sleep(1);
  35.         }
  36.         //等价于pid_t ret = wait(NULL);
  37.         pid_t ret =waitpid(-1,NULL,0);//我们先这么用着
  38.         if(ret == id)//id是父进程fork的返回值——子进程的PID
  39.         {
  40.             printf("wait success, ret : %d\n",ret);
  41.         }
  42.         sleep(5);
  43.     }
  44. }
复制代码
 我们看看运行情况
  

  监督情况
  5个这个
  

  5个这个
  
 5个这个
  

  这个跟wait几乎一模一样
  我们也验证了返回值
   

  我们现在不来谈讨僵尸进程,我们现在来讨论子进程的退出码,这个是必要status参数的
  wait和waitpid都有这个status参数
  4..参数status——获得子进程退出情况

        当子进程停止后,wait函数会返回子进程的进程ID(PID),并将子进程的停止状态存储在指针status指向的变量中。

                                  status是个输出型参数
  什么叫输出型参数?就是函数调用竣事以后,会将参数的值写到这个变量里。
  
          换句话说,这个status是用来接收的,本质上不是用来传参的。
          我们把我们的status界说好了之后,放到该函数里,作为参数通报已往,函数调用完后,操作系统就会把status的值自动添补好,然后还给我们。实现的原理很简单,因为其用的是指针,通报的是变量的地址。倘若我们不关心这个status状态,那么直接通报NULL即可。
            但如果我们对这个子进程是如何死掉毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就像下面这样:
  1. pid = wait(NULL);
复制代码
如果乐成,wait会返回被网络的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。
  我们现在来相识一下这个参数的用法
  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<sys/types.h>
  5. #include<sys/wait.h>
  6. int main()
  7. {
  8.     pid_t id=fork();
  9.     if(id<0)
  10.     {
  11.         perror("fork\n");
  12.         return 1;
  13.     }
  14.     else if(id==0)
  15.     {
  16.     //child
  17.     int cnt=5;
  18.     while(cnt)
  19.         {
  20.             printf("I am child ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  21.             cnt--;
  22.             sleep(1);
  23.         }
  24.         exit(1);//子进程在这里退出,注意是1
  25.     }
  26.     else
  27.     {
  28.     //parent
  29.       int cnt=10;
  30.       while(cnt)
  31.         {
  32.             printf("I am father ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  33.             cnt--;
  34.             sleep(1);
  35.         }
  36.         int status=0;//定义一个整型变量
  37.         //等价于pid_t ret = wait(NULL);
  38.         pid_t ret =waitpid(id,&status,0);//注意这个id
  39.         
  40.         if(ret == id)//id是父进程fork的返回值——子进程的PID
  41.         {
  42.             printf("wait success, ret : %d,status: %d\n",ret,status);//把status打印出来看看
  43.         }
  44.         sleep(5);
  45.     }
  46. }
复制代码
我们看看运行情况
 我们看看监控情况
5个这个

5个这个

5个这个

   留意:我们说的status,不是参数status,而是status指向的谁人整数 
    为什么status是256? 我们先不讲,先回答几个问题
     1.子进程在这里有几种退进场景?
  

    2.父进程盼望获得子进程的哪些信息?
  
  一个参数要纪录这么多东西,我靠??? 吃得消吗?
正因为这个status这个天赋苦命圣体,以是它得被拆分成几个部分,分别纪录差别的信息
   留意:我们说的status,不是参数status,而是status指向的谁人整数  
  2.2.1.status 参数是位图结构 
   这个小小的数字,怎么存下这么多消息?
  它虽然是一个 int 型整数,但是不能简单地将其看作整型,而是被当作一个 位图结构 对待。
不过,关于 status 我们只必要关心该整数的 低 16 个比特位!
int有32个比特位,我们不必去关心它的高 16 位,因为依附低 16 位就足以判断了。
然而,整数的低 16 位,此中又可以分为 最低八位 和 次低八位(具体细节看图):

我们之研究status的低16比特位
 2.2.2、次低八位:存储了子进程退出码
重点:通过提取 status 的次低八位(倒数第9-16位),就可以拿到子进程的退出码。 
2.2.3、 最低七位:提取子进程的退出信号
重点:通过提取 status 的最低七位,就可以拿到子进程的退出信号。
我们的 status 的最低八位用于表示处理异常的地方,此中有 1 位是 core dump,我们等到信号那节再讲。
撤消 core dump,剩余七位用于进程中的退出信号,这就是 最低七位。
   进程退出,如果异常退出,是因为这个进程收到了特定的信号。
  

   退出状态,没有0号信号,进程信号是0就代表进程没有异常 
    有人就问了,搞这么复杂,能不能直接使用几个全局变量来实现这一功能?
  
   我们用代码
  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<sys/types.h>
  5. #include<sys/wait.h>
  6. int main()
  7. {
  8.     pid_t id=fork();
  9.     if(id<0)
  10.     {
  11.         perror("fork\n");
  12.         return 1;
  13.     }
  14.     else if(id==0)
  15.     {
  16.     //child
  17.     int cnt=5;
  18.     while(cnt)
  19.         {
  20.             printf("I am child ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  21.             cnt--;
  22.             sleep(1);
  23.         }
  24.         exit(1);//子进程在这里退出,注意是1
  25.     }
  26.     else
  27.     {
  28.     //parent
  29.       int cnt=10;
  30.       while(cnt)
  31.         {
  32.             printf("I am father ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  33.             cnt--;
  34.             sleep(1);
  35.         }
  36.         int status=0;//定义一个整型变量
  37.         //等价于pid_t ret = wait(NULL);
  38.         pid_t ret =waitpid(id,&status,0);//注意这个id
  39.         
  40.         if(ret == id)//id是父进程fork的返回值——子进程的PID
  41.         {
  42.             //7F:0111 1111,status&7F就是status的倒数第七位
  43.             printf("wait success, ret : %d,exit sign: %d,exit code:%d\n",
  44.                     ret,status&0x7F,(status>>8)&0xFF);
  45.         }
  46.         sleep(5);
  47.     }
  48. }
复制代码
我知道各人对status&0x7F和(status>>8)&0xFF很好奇
    这个是退出信号的
  当status是一个32位(比特位)的整数,而且我们实行status & 0x7F操作时,我们现实上是在从status中提取其最低的7位。
  
    这个是退出码的
  表达式 (status >> 8) & 0xFF 是一种位操作,这个表达式具体做了两件事情:
    综上所述(status >> 8) & 0xFF 这个表达式的目标是从 status 中提取出右移8位之后的新低8位(即原 status 的第9位到第16位,如果我们从0开始计数的话)。
  1. ​exitCode = (status >> 8) & 0xFF; //退出码
  2. exitSignal = status & 0x7F;      //退出信号
复制代码
 我们运行一下

   退出信号是0,退出码是1,这个1从那边来? 
   

这个exit(1)就是源头
    我们来让子进程出一次异常,看看会是什么情况?
  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<sys/types.h>
  5. #include<sys/wait.h>
  6. int main()
  7. {
  8.     pid_t id=fork();
  9.     if(id<0)
  10.     {
  11.         perror("fork\n");
  12.         return 1;
  13.     }
  14.     else if(id==0)
  15.     {
  16.     //child
  17.     int cnt=5;
  18.     while(cnt)
  19.         {
  20.             printf("I am child ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  21.             cnt--;
  22.             sleep(1);
  23.         }
  24.         int a=10; a/=0;//注意这里
  25.         exit(1);//子进程在这里退出,注意是1
  26.     }
  27.     else
  28.     {
  29.     //parent
  30.       int cnt=10;
  31.       while(cnt)
  32.         {
  33.             printf("I am father ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  34.             cnt--;
  35.             sleep(1);
  36.         }
  37.         int status=0;//定义一个整型变量
  38.         //等价于pid_t ret = wait(NULL);
  39.         pid_t ret =waitpid(id,&status,0);//注意这个id
  40.         
  41.         if(ret == id)//id是父进程fork的返回值——子进程的PID
  42.         {
  43.             //7F:0111 1111,status&7F就是status的倒数第七位
  44.             printf("wait success, ret : %d,exit sign: %d,exit code:%d\n",
  45.                     ret,status&0x7F,(status>>8)&0xFF);
  46.         }
  47.         sleep(5);
  48.     }
  49. }
复制代码

实在子进程实行一次处0错误,就立马奔溃了
 异常信号是8,8是什么?

8就是浮点数异常
   为什么要通过系统调用来获得子进程的退出情况?父进程不能直接获取吗?
  父,子进程在操作系统中是相互独立的,都有自己的数据结构
        我们讲个故事,小张是A学校的,他的编程本领很强,隔壁B学校想让小张替他们学校去到场一次比赛,那就必须和A学校的校长沟通,才华让小张去到场
那么父进程就相称于B学校,子进程是小张
   进程在什么时间会等待失败?
  这个情况一样平常是等待的进程不是自己的子进程
  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<sys/types.h>
  5. #include<sys/wait.h>
  6. int main()
  7. {
  8.     pid_t id=fork();
  9.     if(id<0)
  10.     {
  11.         perror("fork\n");
  12.         return 1;
  13.     }
  14.     else if(id==0)
  15.     {
  16.     //child
  17.     int cnt=5;
  18.     while(cnt)
  19.         {
  20.             printf("I am child ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  21.             cnt--;
  22.             sleep(1);
  23.         }
  24.         exit(1);//子进程在这里退出,注意是1
  25.     }
  26.     else
  27.     {
  28.     //parent
  29.       int cnt=10;
  30.       while(cnt)
  31.         {
  32.             printf("I am father ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  33.             cnt--;
  34.             sleep(1);
  35.         }
  36.         int status=0;//定义一个整型变量
  37.         //等价于pid_t ret = wait(NULL);
  38.         pid_t ret =waitpid(id+4,&status,0);//注意这个id
  39.         
  40.         if(ret == id)//id是父进程fork的返回值——子进程的PID
  41.         {
  42.             //7F:0111 1111,status&7F就是status的倒数第七位
  43.             printf("wait success, ret : %d,exit sign: %d,exit code:%d\n",
  44.                     ret,status&0x7F,(status>>8)&0xFF);
  45.         }
  46.         else
  47.         {
  48.         printf("wait filled\n");
  49.         }
  50.         sleep(5);
  51.     }
  52. }
复制代码

很明显了,等待失败了 
4.1、进程退出的宏

我们在上面使用status&0x7F和(status>>8)&0xFF来获取进程的退出信息,但是并不必要这么贫苦
我们今天写的代码,是通过位操作去截 status 得到退出码和退出信号的。
现实上,你也可以不消位操作,因为  已经给我们提供了一些宏供我们直接调用。
它们是 WEXITSTATUS 和 WIFEXITED,在这之前,我们再思索一个问题:
   思索:一个进程退出时,可以拿到退出码和退出信号,我们先看谁?
          一旦程序发现异常,我们只关心退出信号,退出码没有任何意义。
          以是,我们先关注退出信号,如果有异常了我们再去关注退出码。
    如果参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入此中, 这是一个整数值(int),指出了子进程是正常退出还是被非正常竣事的,以及正常竣事时的返回值,或被哪一个信号竣事的等信息。
        由于这些信息 被存放在一个整数的差别二进制位中,以是用通例的方法读取会非常贫苦,人们就筹划了一套专门的宏(macro)来完成这项工作,下面我们来学习一下此中最常用的两个:
   
          (请留意,虽然名字一样,这里的参数status并差别于wait唯一的参数——指向整数的指针status,而是谁人指针所指向的整数,切记不要搞混了。)
   
  当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的,如果子进程调用exit(5)退出,WEXITSTATUS(status) 就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。
            请留意,如果进程不是正常退出的,也就是说, WIFEXITED返回0,WEXITSTATUS(status)就毫偶然义。
  以是我们先判断程序也没有出异常,再看退出码,如果出现了异常,退出码就不看了
就先看WIFEXITED(status)!=0,如何打印 WEXITSTATUS(status)
我们看个例子 
  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<sys/types.h>
  5. #include<sys/wait.h>
  6. int main()
  7. {
  8.     pid_t id=fork();
  9.     if(id<0)
  10.     {
  11.         perror("fork\n");
  12.         return 1;
  13.     }
  14.     else if(id==0)
  15.     {
  16.     //child
  17.     int cnt=3;
  18.     while(cnt)
  19.         {
  20.             printf("I am child ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  21.             cnt--;
  22.             sleep(1);
  23.         }
  24.         exit(1);//子进程在这里退出,注意是1
  25.     }
  26.     else
  27.     {
  28.     //parent
  29.       int cnt=5;
  30.       while(cnt)
  31.         {
  32.             printf("I am father ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  33.             cnt--;
  34.             sleep(1);
  35.         }
  36.         int status=0;//定义一个整型变量
  37.         //等价于pid_t ret = wait(NULL);
  38.         pid_t ret =waitpid(id,&status,0);//注意这个id
  39.         
  40.         if(ret == id)//id是父进程fork的返回值——子进程的PID
  41.         {
  42.             if(WIFEXITED(status)!=0)
  43.             {
  44.             printf("child is run successfull,exit code:%d\n",WEXITSTATUS(status));
  45.             }
  46.         }
  47.         else
  48.         {
  49.         printf("wait filled\n");
  50.         }
  51.         sleep(3);
  52.     }
  53. }
复制代码

完美啊!!!!!还不消起来??
我们再来看看多个子进程来
  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<sys/types.h>
  5. #include<sys/wait.h>
  6. #define N 10
  7. void RunChild()
  8. {
  9.     int cnt =5;
  10.     while(cnt)
  11.     {   
  12.         printf("I am child,pid:%d,ppid:%d\n",getpid(),getppid());
  13.        cnt--;
  14.         sleep(1);
  15.     }
  16. }
  17.         
  18. int main()
  19. {
  20.     int i;
  21.     for(i=0;i<N;++i)
  22.     {
  23.     pid_t id =fork();
  24.     if(id==0)
  25.         {
  26.      RunChild();
  27.         exit(i);//注意这里
  28.         }
  29.     printf("creat child :%d  sucess\n",getpid());//这句话只有父进程会执行
  30.     }
  31.    
  32.     sleep(10);
  33.    
  34.     //等待,我们这里有10个子进程
  35.     for(i=0;i<N;++i)
  36.     {
  37.     int status =0;
  38.     pid_t ret =waitpid(-1,&status,0);
  39.     if(ret > 0)
  40.         {
  41.         printf("wait %d success,exit code: %d\n",ret,WEXITSTATUS(status));
  42.         }
  43.     }
  44.     sleep(5);//父进程最后等一下
  45. }
复制代码

这就是进程等待,必须通过父进程来举行等待
5.option参数

当前设置等待的方式——有两种重要的等待方式

   5.1.阻塞等待——对应参数0

  
  5.2.阻塞等待和非阻塞等待的区分

场景:张三找李四告急帮他复习期末考试。
张三在李四的楼下等待李四就绪。
   
非阻塞等待

  张三每隔几分钟就给李四打电话扣问他是否就绪了,张三在没有打电话的时间看书/游戏/抖音
    
   
阻塞等待

          张三不绝给李四打着电话,直到李四就绪,期间张三不绝等待李四就绪,不敢别的事情。不绝检测李四的状态(不就绪,就不返回)
      不绝等待。直到子进程停止才返回。
    5.3. 非阻塞轮询——对应参数WNOHANG

   在子进程运行期间,父进程除了等待子进程或者是休眠,能不能干点其他的事情❓
          当然可以,在父进程等待,阻塞状态。可以通过设置options来让父进程干点事情。不阻塞等待而是非阻塞等待。
    什么是非阻塞等待呢?用代码该怎么去实现呢?
    非阻塞等待(Non-blocking Wait)则与阻塞等待相反。
  
          通过设置options的宏值WNOHANG(wait no hang 等待没有阻塞 = 非阻塞等待)
  在盘算机中,"HANG" 通常指的是程序或系统出现无响应或停顿的状态,也就是常说的“卡住”或“死机”。
          当程序或系统由于某种原因(如资源锁定、死循环、死锁或外部系统交互问题等)而无法继续正常实行时,就可能会出现"HANG"的情况。这种情况下,用户可能无法与程序或系统举行交互,必要等待程序或系统恢复正常或举行重启操作。
          另外,在一些特定的语境下,"HANG" 也可能被用来形貌服务器或数据库的某些服务出现故障或无法访问的情况,这也可以被视为一种"宕机"征象。在这种情况下,"HANG" 指的是服务器或数据库的服务因为某种原因而制止响应或无法提供服务。
    具体操作
      综上:非阻塞等待的时间 + 循环 = 非阻塞轮询
  
看个例子 


  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<sys/types.h>
  5. #include<sys/wait.h>
  6. int main()
  7. {
  8.     pid_t id=fork();
  9.     if(id<0)
  10.     {
  11.         perror("fork\n");
  12.         return 1;
  13.     }
  14.     else if(id==0)
  15.     {
  16.     //child
  17.     int cnt=5;
  18.     while(cnt)
  19.         {
  20.             printf("I am child ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  21.             cnt--;
  22.             sleep(1);
  23.         }
  24.         exit(1);//子进程在这里退出,注意是1
  25.     }
  26.     else
  27.    {
  28.     //parent
  29.         int status=0;//定义一个整型变量
  30.     while(1)
  31.     {   //等价于pid_t ret = wait(NULL);
  32.         pid_t ret =waitpid(id,&status,WNOHANG);//注意这个id
  33.         
  34.         if(ret >0)//等待成功
  35.         {
  36.             if(WIFEXITED(status)!=0)
  37.             {
  38.             printf("child is run successfull,exit code:%d\n",WEXITSTATUS(status));
  39.             }
  40.             break;//成功了就撤啊
  41.         }
  42.         else if(ret<0)//还需要等待
  43.         {
  44.         printf("wait filled\n");
  45.           break;//失败了就赶紧跑
  46.         }
  47.         else
  48.         {
  49.         printf("child is no exit,I am waitting...\n");
  50.         sleep(1);//父进程再等等子进程
  51.         }
  52.     }
  53.         sleep(3);
  54.    }
  55. }
复制代码
 

很完美吧
我们加了sleep(1)来加长父进程等待的时间。我们也可以不等待,也可以干别的事情
   我们只需在waitpid返回值为0的地方设置好父进程该干的事情即可
  我们来将等待换成别的任务
  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<sys/types.h>
  5. #include<sys/wait.h>
  6. int main()
  7. {
  8.     pid_t id=fork();
  9.     if(id<0)
  10.     {
  11.         perror("fork\n");
  12.         return 1;
  13.     }
  14.     else if(id==0)
  15.     {
  16.     //child
  17.     int cnt=3;
  18.     while(cnt)
  19.         {
  20.             printf("I am child ,pid:%d, ppid :%d,cnt: %d\n",getpid(),getppid(),cnt);
  21.             cnt--;
  22.             sleep(1);
  23.         }
  24.         exit(1);//子进程在这里退出,注意是1
  25.     }
  26.     else
  27.    {
  28.     //parent
  29.         int status=0;//定义一个整型变量
  30.     while(1)
  31.     {   //等价于pid_t ret = wait(NULL);
  32.         pid_t ret =waitpid(id,&status,WNOHANG);//注意这个id
  33.         
  34.         if(ret >0)//等待成功
  35.         {
  36.             if(WIFEXITED(status)!=0)
  37.             {
  38.             printf("child is run successfull,exit code:%d\n",WEXITSTATUS(status));
  39.             }
  40.             break;//成功了就撤啊
  41.         }
  42.         else if(ret<0)//还需要等待
  43.         {
  44.         printf("wait filled\n");
  45.           break;//失败了就赶紧跑
  46.         }
  47.         else//干别的事情
  48.         {
  49.         int i=0;
  50.         for(;i<4;++i)
  51.         {
  52.         printf("Father is doing other things:%d\n",i);
  53.         sleep(1);
  54.         }
  55.     }
  56.         sleep(2);
  57.    }
  58. }
复制代码
 
怎么样呢?

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4