当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。因为子进程终止是个异步事件。所以这种信号也是内核向父进程发的异步通知。父进程可以选择忽略该信号,或者提供一个该信号发生时即使被调用执行的函数。对于这种信号的系统默认动作时忽略它。对于wait和waitpid函数。
如果其所有子进程都还在运行,则阻塞。
如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。
如果它没有任何子进程,则立即出错。
如果进程由于接收到SIGCHLD信号而调用wait,我们期望wait会立即返回。但是如果在随机时间点调用wait,则进程可能会阻塞。
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
两个函数返回值:若成功,返回进程ID;若出错,返回0或-1
这两个函数的区别如下:
在一个子进程终止前,wait使其调用者阻塞,而waitpid有一选项,可使调用者不阻塞。
waitpid并不等待在其调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程。
如果子进程已经终止,并且是一个僵死进程,则wait立即返回并取得该子进程的状态。否则则wait使其调用者阻塞,直到一个子进程终止。如调用者阻塞而且它有多个子进程,则在其某一个子进程终止时,wait就立即返回。因为wait返回终止子进程的进程ID,所以它总能了解是哪一个子进程终止了。
这两个函数的参数statloc是一个整型指针。如果statloc不是一个空指针,则终止进程的终止状态就存放在它所指向的单元内。如果不关心终止状态,则可将该参数指定为空指针。
依据传统,这两个函数返回的整型状态字是由实现定义的。其中某些为表示退出状态,其他位则指示信号编号,有一位指示是否产生了core文件等。POSIX.1规定,终止状态用定义在<sys/wait.h>中的各个宏来查看。有4个互斥的宏可用来取的进程终止的原因,它们的名字都以WIF开始。基于这4个宏中哪一个值为真,就可选其他宏来取得退出状态、信号编号等。这4个互斥的宏表示于下图:
宏 | 说明 |
WIFEXITED(status) | 若为正常终止子进程返回的状态,则为真。对于这种情况可执行WEXITSTATUS(status),获取子进程传送给exit或_exit参数的低8位 |
WIFSIGNALED(status) | 若为异常终止子进程返回的状态,则为真。对于这种情况,可执行WTERMSIG(status),获取使子进程终止的信号编号。另外,有些实现定义宏WCOREDUMP(status),若已产生终止进程的core文件,则它返回真 |
WIFSTOPPED(status) | 若为当前暂定子进程的返回的状态,则为真。对于这种情况,可执行WSTOPSIG(status),获取使子进程暂停的信号编号 |
WIFCONTINUEO(status) | 若在作业控制暂停后已经继续的子进程返回了状态,则为真,仅用于waitpid |
实例:
#include "apue.h"
#include <sys/wait.h>
void pr_exit(int status)
{
if(WIFEXITED(status))
printf("normal termination, exit status = %d\n",WEXITSTATUS(status));
else if(WIFSIGNALED(status))
printf("abnormal termination, signal number = %d%s\n",WTERMSIG(status),
#ifdef WCOREDUMP
WCOREDUMP(status) ? " (core file generated)" : "");
#else
"");
#endif
else if(WIFSTOPPED(status))
printf("child stopped, signal number = %d\n",WSTOPSIG(status));
}
int main(void)
{
pid_t pid;
int status;
if((pid = fork()) < 0)
err_sys("fork error");
else if(pid == 0)
exit(7);
if(wait(&status) != pid)
err_sys("wait error");
pr_exit(status);
if((pid = fork()) < 0)
err_sys("fork error");
else if(pid == 0)
abort();
if(wait(&status) != pid)
err_sys("wait error");
pr_exit(status);
if((pid = fork()) < 0)
err_sys("fork error");
else if(pid == 0)
status /=0;
if(wait(&status) != pid)
err_sys("wait error");
pr_exit(status);
exit(0);
}
运行程序:
# ./a.out
normal termination, exit status = 7
abnormal termination, signal number = 6
abnormal termination, signal number = 8
我们从WTERMSIG中打印信号编号。可以查看<signal.h>头文件验证SIGABRT的值为6,SIGFPE的值为8.
正如前面所述,如果一个进程有几个子进程,那么只要有一个进程终止,wait就返回。POSIX.1定义了waitpid函数用于等待一个指定的进程终止,对于waitpid函数中pid参数的作用解释如下。
pid == -1 等待任一子进程。此种情况下,waitpid与wait等效。
pid >0 等待进程ID与pid相等的子进程。
pid == 0 等待组ID等于调用进程组ID的任一子进程。
pid < -1 等待组ID等于pid绝对值的任一子进程。
waitpid函数返回终止子进程的进程ID,并将该子进程的终止状态放在由statloc指向的存储单元中。对于wait,其唯一的出错是调用进程没有子进程。但是对于waitpid,如果指定的进程或进程组不存在,或者参数pid指定的进程不是调用进程的子进程,都可能出错。
options参数使我们能进一步控制waitpid的操作,此参数或者时0,或者是下表的常量值按或运算的结果。
常量 | 说明 |
WCONTINUED | 若实现支持作业控制,那么由pid制定的任一子进程在停止后已经继续,但其状态尚未报告,则返回其状态 |
WNOHANG | 若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,此时其返回值为0 |
WUNTRACED | 若某实现支持作业控制,而由pid指定的任一子进程已处于停止状态,并且其状态自停止以来还未报告过,则返回其状态,WIFSTOPPED宏确定返回值是否对应于一个停止的子进程。 |
(1)waitpid可等待一个特定的进程,而wait则返回任一终止子进程的状态。
(2)waitp提供了一个wait的非柱塞版本。有时希望获取一个子进程的状态,但不想阻塞。
(3)waitpid通过WUNTRACED和WCONTINUED选项支持作业控制。