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

本文主要是介绍【进程等待】waitpid的参数pid | status的位图位操作WIFEXITEDWEXITSTATUS宏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

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   //father30   //父进程,父进程在子进程运行期间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演示】

//father31   //父进程,父进程在子进程运行期间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   else38   {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   //father30   //父进程,父进程在子进程运行期间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   else38   {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.c1 #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   //father30   //父进程,父进程在子进程运行期间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     else40     {41       printf("child quit unnormal!\n");42     }43     printf("wait success,rid: %d\n",rid);                                                                44   }45   else46   {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的最后一个参数。

这篇关于【进程等待】waitpid的参数pid | status的位图位操作WIFEXITEDWEXITSTATUS宏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/974628

相关文章

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

4B参数秒杀GPT-3.5:MiniCPM 3.0惊艳登场!

​ 面壁智能 在 AI 的世界里,总有那么几个时刻让人惊叹不已。面壁智能推出的 MiniCPM 3.0,这个仅有4B参数的"小钢炮",正在以惊人的实力挑战着 GPT-3.5 这个曾经的AI巨人。 MiniCPM 3.0 MiniCPM 3.0 MiniCPM 3.0 目前的主要功能有: 长上下文功能:原生支持 32k 上下文长度,性能完美。我们引入了

[Linux]:进程(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 进程终止 1.1 进程退出的场景 进程退出只有以下三种情况: 代码运行完毕,结果正确。代码运行完毕,结果不正确。代码异常终止(进程崩溃)。 1.2 进程退出码 在编程中,我们通常认为main函数是代码的入口,但实际上它只是用户级

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代,文本到语音(Text-to-Speech, TTS)技术已成为人机交互的关键桥梁,无论是为视障人士提供辅助阅读,还是为智能助手注入声音的灵魂,TTS 技术都扮演着至关重要的角色。从最初的拼接式方法到参数化技术,再到现今的深度学习解决方案,TTS 技术经历了一段长足的进步。这篇文章将带您穿越时

如何确定 Go 语言中 HTTP 连接池的最佳参数?

确定 Go 语言中 HTTP 连接池的最佳参数可以通过以下几种方式: 一、分析应用场景和需求 并发请求量: 确定应用程序在特定时间段内可能同时发起的 HTTP 请求数量。如果并发请求量很高,需要设置较大的连接池参数以满足需求。例如,对于一个高并发的 Web 服务,可能同时有数百个请求在处理,此时需要较大的连接池大小。可以通过压力测试工具模拟高并发场景,观察系统在不同并发请求下的性能表现,从而

java 进程 返回值

实现 Callable 接口 与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。 public class MyCallable implements Callable<Integer> {public Integer call() {return 123;}} public static void main(String[] args

多路转接之select(fd_set介绍,参数详细介绍),实现非阻塞式网络通信

目录 多路转接之select 引入 介绍 fd_set 函数原型 nfds readfds / writefds / exceptfds readfds  总结  fd_set操作接口  timeout timevalue 结构体 传入值 返回值 代码 注意点 -- 调用函数 select的参数填充  获取新连接 注意点 -- 通信时的调用函数 添加新fd到

C#关闭指定时间段的Excel进程的方法

private DateTime beforeTime;            //Excel启动之前时间          private DateTime afterTime;               //Excel启动之后时间          //举例          beforeTime = DateTime.Now;          Excel.Applicat