【进程等待】waitpid的参数pid | status的位图&位操作&WIFEXITED&WEXITSTATUS宏

目录

waitpid

pid

status

status位图 

status按位操作

输入型参数和输入型参数 

宏WIFEXITED&WEXITSTATUS

options&非阻塞等待


上篇进程等待我们介绍到怎样去进程等待。我们介绍了wait函数&阻塞等待。本篇我们将介绍waitpid函数的参数pid和status。

waitpid

  • man 2 waitpid
  • 等待一个进程的状态发生变化
  • pit_t pid是父进程等待的子进程的pid,情况有3种,这里介绍两种情况。
  • int *status是NULL
  • int options是0
  • 先把后面两个参数设置为NULL和0

pid_ t waitpid(pid_t pid, int *status, int options);
返回值

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

参数
pid:

  • Pid=-1,等待任一个子进程。与wait等效。
  • Pid>0.等待其进程ID与pid相等的子进程。

status:

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

options:

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

waitpid是一个在Unix和类Unix系统(如Linux)中常用的系统调用,用于等待一个或多个子进程的状态改变。与wait系统调用相比,waitpid提供了更多的灵活性,因为它允许你指定要等待的子进程的PID(进程ID),并可以设置等待的模式(阻塞或非阻塞)。

以下是waitpid函数的原型和参数详解:

pid_t waitpid(pid_t pid, int *status, int options);
  • pid:这是一个整数,用于指定要等待的子进程的PID。其取值有以下几种情况:
    • pid > 0:等待指定PID为pid的子进程。
    • pid = 0:等待当前进程组中的任何子进程。
    • pid = -1:等待任何子进程,与wait函数的功能相同。
    • pid < -1:等待组ID为-pid的任意子进程。
  • status:这是一个指向整数的指针,用于存储子进程的退出状态。如果不需要这些信息,可以将此参数设置为NULL。
  • options:这是一个位掩码,用于修改waitpid的行为。常见的选项有:
    • WNOHANG:使waitpid调用变为非阻塞。如果没有子进程满足条件,waitpid会立即返回。
    • WUNTRACED:当子进程被停止时(而不是退出时),也返回它的状态。这通常与跟踪被调试的子进程有关。
    • WCONTINUED:当被停止的子进程恢复时,返回它的状态。

waitpid的返回值有以下几种情况:

  • 如果成功,返回收集到的子进程的PID。
  • 如果设置了WNOHANG选项并且没有子进程满足条件,返回0。
  • 如果在调用中出现错误,返回-1,并设置全局变量errno以指示错误。

waitpid是一个非常有用的工具,允许你更精细地控制和管理你的子进程。

pid

  • 这样设置waitpid的参数,waitpid(-1,NULL,0)和 wait(NULL)的功能一致
  • pid = -1表示等待任何一个子进程的退出
  • pid > 0表示等待指定的一个子进程的退出
  • pid为错误数字。wait fail !(只要pid不输入错误❌一般父进程都不会等待失败)
  • fork返回值
  1. 0是子进程执行
  2. >0返回的是子进程的pid 是父进程执行(id是子进程的pid数值)

pid:这是一个整数,用于指定要等待的子进程的PID。其取值有以下几种情况:

  • pid > 0:等待指定PID为pid的子进程。
  • pid = 0:等待当前进程组中的任何子进程。
  • pid = -1:等待任何子进程,与wait函数的功能相同。
  • pid < -1:等待组ID为-pid的任意子进程。
 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   pid_t rid = waitpid(id,NULL,0);
 33   if(rid > 0)
 34   {
 35     printf("wait success,rid: %d\n",rid);
 36   } 
 37   sleep(3);
 38   printf("father quit ... \n");
 39 }

【错误的pid演示】

//father
 31   //父进程,父进程在子进程运行期间5ms一直在等待.... 
 32   pid_t rid = waitpid(id+1 , NULL , 0);
 33   if(rid > 0)
 34   {
 35     printf("wait success,rid: %d\n",rid);
 36   }
 37   else
 38   {
 39     printf("wait fail !\n");
 40   }
 41   sleep(3);
 42   printf("father quit ... \n");                     

 

status

status:这是一个指向整数的指针,用于存储子进程的退出状态。如果不需要这些信息,可以将此参数设置为NULL。

  • int *status表示通过等待获取子进程的退出信息。(❗前面的进程终止已知进程终止的退出信息表示退出码&退出信号
  • 父进程通过waitpid等待子进程,通过第二个参数就可以拿到子进程的退出信息(status是输出型参数)
  • 当然也存在父进程不关心子进程的退出信息,&status直接置为NULL即可。(等待是必须的,但是获取子进程的退出信息不是必须的)
  • status不能简单的当作整形来看待,可以当作位图来看待,存在特定的格式。
  • status是输出型参数。
  • status按位操作拿到退出码和退出信号。
  • status通过下面两个宏拿到退出码和退出信号。
  1. WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  2. WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码。

有人问可不可以不用waitpid传递子进程的退出信息,直接定义两个全局变量exit_code,exit_signal。在子进程退出的时候写入自己的退出信息到全局变量当中❓

  • 当然不可以,全局也是数据。虽然一开始是被父子进程共享,当子进程要写入的时候,会发生写时拷贝,此刻父子进程都有一份各自的全局变量的数据,父进程是看不到的子进程写入自己的全局变量的数据。
  • 所以只能通过waitpid的status参数。下面我们来仔细学习以下☞☞

status位图 

怎么把子进程的退出信息(退出码和退出信号)整合成一个数字赋值给status呢❓

  • wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息。
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status不能简单的当作整形来看待,可以当作位图来看待,存在特定的格式,32比特位具体细节如下图(只研究status低16比特位)
  1. 32比特位只使用低16比特位
  2. 其中次低8位:进程退出时所对应的退出状态(退出码)
  3. 其中最低7位:进程退出时是否受到终止信号(退出信号)
  4. 第8位:暂时不考虑,后面进程信号会讲
  • 所以,退出状态的取值范围是[0,255],退出信号取值范围:[0,127]
  • status是以位图的形式把子进程的退出码和退出信息返回给父进程的

在Linux的waitpid函数中,status参数是一个指向整数的指针,它用于存储子进程的退出状态信息。尽管status本身是一个整型指针,但我们更关心的是它所指向的那个整数变量的取值范围和内容。

  • 这个整数变量(我们称之为status_val)通常用于表示子进程的退出状态。在大多数情况下,我们只关心这个整数的低16位,因为高16位通常不被使用。
  • 正常退出:如果子进程正常退出(即调用了exit()函数),那么status_val的低16位的高8位将保存子进程的返回值(范围是0-255)。低8位中的低7位将被设置为0,而最高位(第8位)通常被用来表示一个标志位,以区分是正常退出还是由于信号而退出。
  • 异常退出:如果子进程由于接收到一个信号而退出,那么status_val的低16位的低7位将保存导致子进程退出的信号的编号(范围通常是1-127,但取决于系统和信号的具体实现)。同样,最高位(第8位)将用于表示是由于信号而退出。
  • 为了从status_val中提取这些信息,可以使用一些宏(如WEXITSTATUSWTERMSIG等)来进行位操作和提取。

  • 因此,status本身并没有一个固定的取值范围,因为它只是一个指针。但它所指向的那个整数变量(status_val)的取值范围和内容则取决于子进程的退出状态。

 

status按位操作

我们知道子进程的退出码和退出信息被父进程调用waitpid等待时,赋值给status参数。如果我们想要在父进程里面查看详细的子进程的退出码和退出信息🆗请看下面代码。

  • 正常退出:(status>>8)&0xFF
  • 异常退出:status&0x7F
  • 解释☞位操作专题。
//....同上
 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(1);//终止进程,子进程直接僵尸
 28   }
 29   //father
 30   //父进程,父进程在子进程运行期间5ms一直在等待.... 
 31   int status = 0;
 32   pid_t rid = waitpid(id, &status, 0);
 33   if(rid > 0)
 34   {
 35     printf("wait success,rid: %d\n",rid);
 36   }
 37   else
 38   {
 39     printf("wait fail !\n");
 40   }
 41   sleep(3);
 42   printf("father quit,status: %d,code: %d,signal: %d\n",status, 
      (status>>8)&0XFF,status&0X7F);            
 43 }

【kill -9 pid错误测试】

【野指针错误测试】

输入型参数和输入型参数 

在函数或系统调用的参数中,我们通常区分输入型参数(Input Parameters)和输出型参数(Output Parameters)。这些参数在函数或系统调用的执行过程中扮演不同的角色。

  1. 输入型参数(Input Parameters)

    • 这些参数是函数或系统调用所需的信息或数据,用于执行特定的任务或操作。
    • 它们通常由调用者(Caller)提供,并在函数或系统调用内部被使用。
    • 输入型参数的值在函数或系统调用执行前后不会改变(除非函数或系统调用有特定的功能去修改它们)。
    • waitpid函数中,pidoptions是输入型参数。pid指定了要等待的子进程的PID,而options则设置了等待的行为(如阻塞或非阻塞)。
  2. 输出型参数(Output Parameters)

    • 这些参数用于存储函数或系统调用的结果或返回的数据。
    • 它们通常在函数或系统调用执行前未定义或具有初始值,但在执行后被赋值或更新。
    • 输出型参数的值在函数或系统调用执行后会被改变。
    • waitpid函数中,status是输出型参数。它用于存储子进程的退出状态信息,这些信息在函数执行前是未知的。

总结:输入型参数是函数或系统调用所需的信息,而输出型参数则用于存储函数或系统调用的结果。在调用函数或系统调用时,正确设置输入型参数和处理输出型参数是非常重要的。

【输入型参数VS输出型参数】简而言之,输入型参数是调用者提供,输出型参数是程序赋值提供 

宏WIFEXITED&WEXITSTATUS

除了用status通过按位操作来提取子进程的退出码和退出信号,有没有其他方法❓ 

  • status按位操作不方便,介绍两个宏。(本质上就是status按位操作封装的)
  • WIFEXITED(status): 若为正常终止子进程返回状态,则为真。查看进程是否是正常退出
  • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。查看进程的退出码 
  • WIFEXITED:W:wait  IF:if  EXITED:exited(等待退出)
  • WEXITSTATUS:W:wait  EXIT:exit  STATUS:status(等待退出状态)
  • 本质是:检查退出信号sign位也就是status的低7位是否位0,为0则是正常退出。非0则是异常退出。若是正常退出则若用户愿意可以用WEXITSTATUS查看退出码。
  • ❗可以用这两个宏来判断是否退出为正常。
 1: 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(1);//终止进程,子进程直接僵尸
 28   }
 29   //father
 30   //父进程,父进程在子进程运行期间5ms一直在等待.... 
 31   int status = 0;
 32   pid_t rid = waitpid(id, &status, 0);
 33   if(rid > 0)
 34   {
 35     if(WIFEXITED(status))
 36     {
 37       printf("child quit normal,child exit code: %d\n",WEXITSTATUS(status));
 38     }
 39     else
 40     {
 41       printf("child quit unnormal!\n");
 42     }
 43     printf("wait success,rid: %d\n",rid);                                                                
 44   }
 45   else
 46   {
 47     printf("wait fail !\n");
 48   }
 49   sleep(3);
 50  //printf("father quit,status: %d,code: %d,signal: %d\n",status,(status>>8)&0XFF,status&0X7F);
 51 }

options&非阻塞等待

前面讲到在子进程运行期间,父进程一直在等待,能不能干点别的事情❓  

options:这是一个位掩码,用于修改waitpid的行为。常见的选项有:

  • WNOHANG:使waitpid调用变为非阻塞。如果没有子进程满足条件,waitpid会立即返回。
  • WUNTRACED:当子进程被停止时(而不是退出时),也返回它的状态。这通常与跟踪被调试的子进程有关。
  • WCONTINUED:当被停止的子进程恢复时,返回它的状态。

🙂感谢大家的阅读,若有错误和不足,欢迎指正。下篇阻塞等待和非阻塞等待以及waitpid的最后一个参数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

唐唐思

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

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

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

打赏作者

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

抵扣说明:

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

余额充值