摘要:本文主要讲述内核空间资源的收回,介绍wait和waitid函数的基本使用方法,以及它们之间的差异.
回收内核空间资源 wait和waitid函数
进程退出时释放了用户空间的资源,但是进程PCB并没有释放,这一工作显然不是自己完成,而是由当前进程的父进程完成的.
当一个进程正常退出或异常退出时,内核就向其父进程发送SIGCHLD信号.因为子进程终止是一个异步事件,所以这种信号也是内核向父进程发的异步通知.父进程可以选择忽略该信号(如果父进程设置了SA_NOCLDWAIT标志位(SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号),或者提供一个该信号发生时即被调用执行的函数。父进程可以显示地调用wait()函数和waitpid()函数来完成.
1. wait()和waitid()函数等待子进程结束
函数定义:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
函数说明:
如果等待到任意一个子进程结束,将返回当前结束的子进程PID,同时将子进程退出时的状态存储在“_stat_loc"变量中.如果出错返回-1,错误原因存储在errno中.
调用wait()函数的父进程,如果其所有的子进程都还在运行,则父进程会阻塞,等待任意一个子进程结束,回收该子进程的内核进程资源.如果父进程没有任何子进程,则会出错.
如果进程由于接收到SIGCHLD信号而调用wait(),则可期望wait()会立即返回.但是如果在任意时刻调用wait(),则进程可能会阻塞.
参数status:
参数status是一个整型指针.如果status不是空指针,则终止进程的终止状态就放在它所指向的内存单元内.如果不关心终止进程的状态,则可将参数指定为空指针.
2. wait()和waitid()函数的区别
(1)在一个子进程终止前,wait()使其调用者阻塞,而waitpid()有一个选项,可使调用者不阻塞.
(2)waitpid()并不等待其在调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程(下文会详细介绍).
例子1:演示wait()函数的基本使用方式
#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/errno.h> int main() { pid_t pid,wait_pid; int status; pid = fork(); //调用fork函数 if(pid==-1) { printf("fork errno:%m\n"); } if(pid==0) { printf("my pid is :%d\n",getpid()); sleep(5); exit(EXIT_SUCCESS); //正常退出 } else { wait_pid = wait(&status); //等待子进程结束 if(WIFEXITED(status)) //调用WIFEXITED宏 { printf("wait on pid:%d,normal exit ,return value is:%4x\n",wait_pid,WEXITSTATUS(status)); } else if(WIFSIGNALED(status)) { printf("wait on pid:%d,recive signal,return value is:%4x\n",wait_pid,WIFSIGNALED(status)); } } return 0; }
程序正常运行退出,输出:
:my pid is :2573
:wait on pid:2573,normal exit ,return value is: 0
如果在程序运行后,向子进程发送一个信号,则输出:
:my pid is :2573
:wait on pid:2573,normal exit ,return value is: 1
在程序中还使用了WIFEXITED和WIFSIGNALED宏,其中宏WIFEXITED是用来判断进程是否是正常退出,如果是,此宏值为1.对于这种情况,可执行WEXITSTATUS(status),取子进程传送给exit,_exit或_Exit参数的低8位.
而宏WIFSIGNALED是用来判断进程是否是因为收到信号后而退出的,如果是,此宏值为1. 对于这种情况,可执行WTERMSIG(status),取使子进程终止的信号编号.
另外还有两个宏与wait和waitpid所返回的终止状态相关的.WIFSTOPPED和WIFCONTINUED.
3. waitpid()函数等待指定的子进程
如果一个进程有几个子进程,那么只要有一个子进程结束终止,wait()函数就会返回.但是现在我们想等待某一个子进程而已,其他子进程结束我们不关心,该怎么办?用户可以使用waitpid()函数来等待指定子进程结束.
定义:pid_t waitpid(pid_t pid, int *status, int options);
其中,第一个参数为进程PID值,对该值有一下的解析:
(1)pid==-1 等待任一子进程结束,此时waitpid()函数等价于wait()函数;
(2)pid>0 等待其进程ID与pid相等的子进程;
(3)pid==0 等待其组ID等于调用进程组ID的任一子进程;
(4)pid<-1 等待其组ID等于pid绝对值得任一子进程.
第二个参数为调用它的函数中某个变量地址,如果执行成功,则用来存储结束进程的结束状态.
第三个参数为等待选项,可以设置为0,亦可为WNOHANG(不阻塞等待)和WUNTRACED(报告状态信息).如果options设置为WNOHANG,而此时没有子进程退出,将立即返回0,不会像wait()那样永远等待下去.否则返回子进程PID,并在参数STAT_LOC中获取子进程的状态.
这是两个常数,可以用"|"运算符把它们连接起来使用,比如:
ret = waitpid(-1,NULL, WNOHANG|WUNTRACED);
如果我们不想使用它们,也可以把options设为0,比如:
ret = waitpid(-1, NULL, 0);
waitpid的返回值
由于waitpid函数参数较多,返回值也是根据参数的情况而定,综合分析一共有3种情况:
(1)如果执行成功正常返回,waitpid()返回子进程的PID;
(2)如果参数options设置了选项WNOHANG,而waitpid在执行时没有已退出的子进程,则返回0;
(3)如果调用出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;当指定的进程或进程组不存在,或者参数pid指定的进程不是调用进程的子进程则出错.
4. waipid()函数提供了wait()函数没有的三个功能
(1)waitpid可等待一个特定的进程,而wait则返回任一终止子进程的状态.
(2)waitpid提供了一个wait的非阻塞版本.又是用户希望取得子进程的状态,但不想阻塞.
(3)waitpid支持作业控制.
例子2:等待指定的子进程
#include <sys/types.h> #include <sys/wait.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> int main() { pid_t chlid_pid, wait_pid; chlid_pid=fork(); if(chlid_pid<0) printf("Error occured on forking.\n"); else if(chlid_pid==0) { sleep(5); exit(0); } do { wait_pid=waitpid(chlid_pid, NULL, WNOHANG); //不阻塞,直接返回 if(wait_pid==0) { printf("No child exited\n"); sleep(1); } }while(wait_pid==0); if(wait_pid==chlid_pid) printf("successfully release child %d\n", wait_pid); else printf("some error occured\n"); return 0; }
输出:
:No child exited
:No child exited
:No child exited
:No child exited
:No child exited
:successfully release child 2538
每隔一秒钟,父进程就检查一次当前是否有子进程退出.在前5秒钟,由于没有子进程退出,父进程调用waitpid(chlid_pid, NULL, WNOHANG)直接返回.子进程退出后,便可以处理其后事.
笔者:个人能力有限,只是学习参考...读者若发现文中错误,敬请提出.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------勿在浮沙筑高台,静下心来,慢慢地沉淀---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------