#include <sys/wait.h> pid_t wait(int *statloc); pid_t waitpid(pid_t pid, int *statloc, int options); /* 两个函数返回值:若成功,返回进程 ID;否则,返回 0 或 -1 */
通常调用这两个函数时:
* 如果其所有子进程都还在运行,则阻塞。
* 如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态后立即返回。
* 如果它没有任何子进程,则立即出错返回。
这两个函数的区别如下。
(1)在一个子进程终止前,wait 使其调用者阻塞,而 waitpid 有一选项可使调用者不阻塞。
(2)waitpid 并不一定要等待在其调用之后的第一个终止子进程,它有若干个可以控制它所等待的进程的选项。
(3)对于 wait,其唯一的出错是调用进程没有子进程(函数调用被一个信号中断时也可能返回另一种出错)。但对于 waitpid,如果指定的进程或进程组不存在,或者 pid 指定的进程不是调用进程的子进程,都可能出错。
它们的参数 statloc 如果不是一个空指针,则子进程的终止状态就存放在它所指向的单元内。如果不关心终止状态,则可将其指定为空指针。依照传统,返回的整型状态字是由实现定义的,其中某些位表示退出状态(正常返回),其他位则指示信号编号(异常返回),有一位指示是否产生了 core 文件等。终止状态可用 <sys/wait.h> 中的各个宏来查看,下表中 4 个互斥的宏可用来取得进程终止的原因。基于这 4 个宏中哪一个为真,就可选用其它宏来取得退出状态、信号编号等(很多平台都支持 WCOREDUMP 宏,但如果定义了 _POSIX_C_SOURCE 常量,有些平台就隐藏该定义)。
下面这个程序就是利用这 4 个宏来打印进程终止状态的说明。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> void pr_exit(int status){ if(WIFEXITED(status)) printf("normal terminaltion, 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){ printf("1st fork error\n"); exit(2); }else if(pid == 0) // child exit(7); if(wait(&status) != pid){ // wait for child printf("1st wait error\n"); exit(2); } pr_exit(status); // and print its status if((pid=fork()) < 0){ printf("2nd fork error\n"); exit(2); }else if(pid == 0) abort(); // generates SIGABRT, signal number is 6 if(wait(&status) != pid){ printf("2nd wait error\n"); exit(2); } pr_exit(status); if((pid=fork()) < 0){ printf("3rd fork error\n"); exit(2); }else if(pid == 0) status /= 0; // generates SIGFPE, signal number is 8 if(wait(&status) != pid){ printf("3rd wait error\n"); exit(2); } pr_exit(status); exit(0); }
运行结果如下。
$ ./exitStatusDemo.out normal terminaltion, exit status = 7 abnormal termination, signal number = 6(core file generated) abnormal termination, signal number = 8(core file generated) $
如果一个进程有几个子进程,那么只要有一个子进程终止,wait 就返回。如果要等待一个指定的进程终止,waitpid 函数就派上用场了。对于 waitpid 函数中的 pid 参数的作用解释如下。
* pid == -1:等待任一子进程,此时等效于 wait。
* pid > 0:等待进程 ID 为 pid 的子进程。
* pid == 0:等待组 ID 等于调用进程组 ID 的任一子进程。
* pid < -1:等待组 ID 等于 pid 绝对值的任一子进程。
参数 options 可以进一步控制 waitpid 的操作。此参数可以为 0 或者是下表中各常量按位或运算的结果(FreeBSD 8.0 和 Solaris 10 支持另一个非标准的可选常量 WNOWAIT,它使系统将终止状态已由 waitpid 返回的进程保持在等待状态,这样它可被再次等待)。
如果一个进程 fork 了一个子进程,但不要它等待子进程终止,也不希望子进程处于僵死状态直到父进程终止,实现这一要求的诀窍就是调用 fork 两次,正如下列这个程序所示。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main(void){ pid_t pid; if((pid=fork()) < 0){ printf("1st fork error\n"); exit(2); }else if(pid == 0){ // first child if((pid=fork()) < 0){ printf("2nd fork error\n"); exit(2); }else if(pid > 0){ exit(0); // parent from second fork == first child } /* We're the second child; our parent becomes init as soon as * our real parent calls exit() in the statement above. Here's * where we'd continue executing, knowing that when we're * done, init will reap our status. */ sleep(2); printf("second child, parent pid=%ld\n", (long)getppid()); exit(0); } if(waitpid(pid, NULL, 0) != pid){ // wait for first child printf("waitpid error\n"); } /* We're the parent(the original process); we continue executing, * knowing that we're not the parent of the second child. */ exit(0); }
执行结果:
$ ./fork2timesDemo.out $ second child, parent pid = 1
注意,这里当原先的进程(即 exec 本程序的进程)终止时,shell 打印其提示符,因为这在第二个子进程打印其父进程 ID 之前发生。
另外,Single UNIX Specification 包含了另一个取得进程终止状态的函数----waitid,此函数类似于 waitpid,但提供了更多的灵活性。
#include <sys/wait.h> int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); /* 返回值:若成功,返回 0;否则,返回 -1 */
waitid 也允许一个进程指定要等待的子进程,但它使用两个单独的参数表示要等待的进程所属的类型,而不是将此与进程 ID 或进程组 ID 组合成一个参数。id 参数的作用与 idtype 的值相关。该函数支持下表中的这几个 idtype 类型。
options 参数是下表中各标志的按位或运算,这些标志指示调用者关注哪些状态变化。
注意,WCONTINUED、WEXITED 和 WSTOPPED 这 3 个常量之一必须指定。
infop 参数是指向 siginfo 结构的指针,该结构包含了造成子进程状态改变有关信号的详细信息。
大多数 UNIX 系统实现还提供了 wait3 和 wait4 函数,它们提供的功能比上面几个 wait 函数提供的要多一个,这与附加参数有关,该参数允许内核返回由终止进程及其所有子进程使用的资源情况。资源统计信息(不同于资源限制)包括用户 CPU 时间总量、系统 CPU 时间总量、缺页次数、接收到信号的次数等,有关细节可查阅 getrusage(2) 手册页。
#include <sys/types.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/resource.h> pid_t wait3(int *statloc, int options, struct rusage *rusage); pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage); /* 两个函数返回值:若成功,返回进程 ID;否则,返回 -1 */