谢谢你的阅读,如有错误请大佬留言
目录
引子:
waitpid
返回值介绍
参数介绍
pid
status
options:
引子:
当一个进程创建子进程后,如果子进程工作结束后会进入僵尸状态,等待父进程回收子进程资源(退出码,退出信号,子进程pid),如果父进程进入死循环或者程序执行时间过长,那么子进程将会一直处于僵尸状态,占用内存空间,这是对我们不利的一种状态,这种状态连kill -9 pid 都无法解决问题,因为你无法杀死一个以及死(Z/D,僵尸状态其实也属于死亡进程,只是未被清理)的状态。那么,如何防止这样的情况呢?
我们可以调用两个系统接口,来处理子进程僵尸状态:
pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options);
让我们先介绍这两个的概念。
看一下代码:
我们的wait和waitpid的作用就是会暂停当前进程的执行,直到有信号到来或者子进程结束。总的来说,wait()的作用就是阻塞父进程,等待子进程。
但是我们的waitpid还有一种非暂停等待子进程的过程。
这里我们主要学waitpid/当我们的waitpid学习后,wait也明白了作用
waitpid
pid_ t waitpid(pid_t pid, int *status, int options);
返回值介绍
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数介绍
pid
1、(重点)pid=-1 等待任意一个子进程退出。
waitpid(-1,NULL,0);
//等待任意的子进程退出
2、(重点)pid>0 等待指定pid进程退出。
waitpid(1234,NULL,0);
//等待pid为1234的子进程退出
(次重)pid=0 等待进程组GID 与当前进程GID的子进程结束(也就是等待同一个进程组中的任何子进程);
waitpid(0,NULL,0);
//等待GID等于当前进程GID的子进程退出
(次重)pid<-1等待进程组识别码为 pid 绝对值的任何子进程。
等待进程组 GID 为 pid 绝对值的进程组中的任何子进程;
GID 是组ID (Group Identify),表示组的身份唯一标识
UID 是用户ID (User Identify),表示用户身份唯一标识
status
先理解实现:
int类型的指针,我们需要在调用处定义。然后传入函数,获取信息。
int sta=0;
waitpid(-1,&sta,0);//返回型参数
这里我们根据sta会取到子进程退出信号,退出码core dump等等信息。为什么一个变量可以获得这么多的信息呢?因为他是一个4字节类型,有32位比特位,所以我们分区块,获取数据,前16位比特位我们暂且不管,我们来看看后16位比特位给我们什么信息。
当进程正常退出,遇见exit/main中return。
退出码位后16位的前8位数据。(正常退出,结果正确/错误)
如果子进程在运行期间发生越界访问或除0等严重错误时,发送信号,程序中断,这时候进程执行不到退出码,所以退出码没有意义,然后后7位为退出的信号
如何查看退出码:先右移8个比特位再位运算
int sta=0;
waitpid(-1,&sta,0);
printf("code:%d",(std>>8)&0xff);//右移后,使用位运算取到退出码
printf("code:%d",WEXITSTATUS(status));//使用宏,其实底层也是位运算,返回子进程错误码。
如何查看退出信号:直接位运算
int sta=0;
waitpid(-1,&sta,0);
printf("code:%d",std&0x7f);//使用位运算取到退出信号,0正常退出,非0退出信号
printf("code:%d",WIFEXITED(status));//使用宏,其实底层也是位运算,
//判断子进程是否正常退出,正常返回真,非正常返回假,bool返回类型
两个宏:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WIFSIGNALED(status):如果子进程异常退出,获取退出信号,正常返回0(查看进程异常退出信号)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
实战使用:
正常退出:
异常退出
发现正常退出时候信号为0,有退出码:123;
而异常退出的时候信号为非0,子进程提前结束无退出码;
options:
0:若pid指定的子进程没有结束,父进程挂起。若正常结束,则返回该子进程的ID。
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。正常结束,则返回该子进程的ID。(异常返回-1)
这里我们介绍一下什么是进程阻塞的概念。
阻塞:指有障碍而不能通过,无法畅通。我们waitpid一般设置为阻塞状态等待子进程返回, 但是我们也可以让进程以非阻塞状态进行等待子进程结束。我第一次理解的时候,这是什么意思?
让我们画图理解下:
A等待B去游玩,但是B在工作,A等待B完成工作
而这的等待有,两个方式:原地等待与边等边做自己的工作。
1、原地等待(阻塞状态):
if(fork()==0)
{
//.....
printf("子进程未结束工作\n");
//.....
exit(0);
}
waitpid(-1,NULL,0);//阻塞状态等待子进程结束
//....
2、边做自己的事情等待(非阻塞)
if(fork()==0)
{
//.....
printf("子进程未结束工作\n");
//.....
exit(0);
}
while(1){
if(0==waitpid(-1,NULL,WNOHANG));//非阻塞状态等待子进程结束
{
//father doing own business
}
else
{
//接收到子进程pid(非0)退出等待
break;
}
}
//....
还记得wait吗
其实wait(NULL)等价于waitpid(-1,NULL,0);阻塞状态等待任意子进程结束,不需要返回值参数。