当一个进程正常或异常终止时,内核会向其父进程发送 SIGCHLD 信号。父进程可以为这个信号提供一个信号处理程序,也可以选择忽略,系统对这种信号默认是忽略。当进程调用 wait 或 waitpid 函数可能会发生以下的情况:注意:如果进程是由于接收到 SIGCHLD 信号而调用 wait 函数,则可期望 wait 立即返回;但是如果是在任意时刻调用 wait,则进程可能会发生阻塞。
/* 等待进程终止 */ /* * 函数功能:等待一个进程终止; * 返回值:若成功则返回进程ID、0;若出错则返回-1; * 函数原型: */ #include <sys/wait.h> pid_t wait(int *statloc); pid_t waitpid(pid_t pid, int *statloc, int option); /* * 说明: * 参数statloc是指向存放终止进程的终止状态单元的指针;如果不在意这些信息,则可设为NULL; * pid和option参数是控制waitpid操作; */ /* * pid参数及作用如下 * pid == -1; 等待任一子进程,此时waitpid和wait功能一样; * pid > 0; 等待其进程ID与pid相等的子进程; * pid == 0; 等待其组ID与调用进程组ID相等的任一子进程; * pid < -1; 等待其组ID与pid绝对值相等的任一子进程; */ /* * 参数option可以是0,也可以是以下参数按位"或"组成: * WCONTINUED 若实现支持作业控制,由pid指定的任一子进程在暂停后已经继续,但其状态尚未报告,则返回其状态; * WNOHANG 若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,此时其返回值0; * WUNTRACED 若某实现支持作业控制,而由pid指定的任一子进程已处于暂停状态,并且其状态自暂停以来还未报告,则返回其状态; */这两个函数的区别如下:
可以用以下宏查看进程的终止状态:
/* 检查 wait 和 waitpid 所返回的终止状态的宏..... * WIFEXITED(status) 若为正常终止子进程返回的状态,则为真;对于这种情况可执行WEXITSTATUS(status), * 取子进程传给exit,_exit或_Exit参数的低8位; * * WIFSIGNALED(status) 若为正常终止子进程返回的状态,则为真;对于这种情况可执行WTERMSIG(status), * 取使子进程终止的信号编号;另外,有些实现定义宏WCONREDUMP(status),若已产生终止进程的core文件,则返回真; * * WIFSTOPPED(status) 若为当前暂停子进程的返回状态,则为真;对于这种情况可执行WSTOPSIG(status),取使子进程暂停的信号编号; * * WIFCONTINUED(status) 若在作业控制暂停后已经继续的子进程返回的状态,则为真; * */测试程序:
#include <sys/wait.h> #include "apue.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 stoped, signal number = %d\n", WSTOPSIG(status)); } int main(void) { pid_t pid; int status; if((pid = fork()) < 0) err_sys("fork error"); else if(0 == pid) /* in child process */ exit(7); if(wait(&status) != pid) /* wait for child process */ err_sys("wait error"); pr_exit(status); /* print its status */ if((pid = fork()) < 0) err_sys("fork error"); else if(0 == pid) /* in child process */ abort(); /* generated SIGABRT */ if(wait(&status) != pid) /* wait for child process */ err_sys("wait error"); pr_exit(status); /* print its status */ if((pid = fork()) < 0) err_sys("fork error"); else if(0 == pid) /* in child process */ status /= 0; /* divide by 0 generated SIGFPE */ if(wait(&status) != pid) /* wait for child process */ err_sys("wait error"); pr_exit(status); /* print its status */ exit(0); }输出结果:
normal termination, exit status = 7 abnormal termination, signal number = 6(core file generated) abnormal termination, signal number = 8(core file generated)输出的结果只是对不同状态的打印,即在正常终止子进程、异常终止子进程、当前暂停子进程的状态。从打印信息可以看到,该子进程发生正常终止和异常终止。
程序2:
#include <sys/wait.h> #include "apue.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 stoped, signal number = %d\n", WSTOPSIG(status)); } int main(void) { pid_t pid,fpid; int status; if((pid = fork()) < 0) err_sys("fork error"); else if(0 == pid) /* in child process */ { printf("child process, sleep 5s.\n"); sleep(5); printf("child process, normal exit.\n"); exit(0); } else { fpid = wait(&status);/* wait for child process */ if(fpid < 0) err_sys("wait error"); printf("father process, child process ID: %d\n",fpid); pr_exit(status); /* print its status */ } exit(0); }以上程序是在现有进程中 fork 创建一个子进程,在 fork 返回的子进程中先睡眠5秒,然后正常退出;在 fork 返回的父进程中,wait 等待子进程终止,然后打印子进程终止状态信息,最后正常退出。输出结果如下:
child process, sleep 5s. child process, normal exit. father process, child process ID: 6065 normal termination, exit status = 0
该函数类似于上面的 waitpid 函数功能,也是允许一个进程指定要等待的子进程,但是 waitid 使用单独的参数表示要等待的子进程的类型,而不是将此与进程 ID 或进程组 ID 组合成一个参数。
/* * waitid 函数 * 函数功能:等待一个进程终止; * 返回值:若成功则返回0,若出错则返回-1; * 函数原型: */ #include <sys/wait.h> int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); /* * 参数说明: * idtype 是以下的常量: * P_PID 等待一个特定的进程:id包含要等待的子进程的进程ID; * P_PGID 等待一个特定进程组的任一子进程:id包含要等待的子进程的进程组ID; * P_ALL 等待任一子进程:忽略id; * * options 是以下参数按位"或"组成: * WCONTINUED 等待一个进程,暂停后已经继续,但其状态尚未报告; * WEXITED 等待已退出的进程; * WNOHANG 若无可用的子进程退出状态,立即返回而非阻塞; * WNOWAIT 不破坏子进程退出状态,该子进程退出状态可由后续的 wait、waitid 或 waitpid 调用取得; * WSTOPPED 等待一个进程,它已经暂停,但其状态尚未报告; * * infop 是指向 siginfo 结构的指针,该结构包含有关引起子进程状态改变的生成信号的详细信息; */程序测试:
#include "apue.h" #include <sys/wait.h> #include <unistd.h> int main(void) { pid_t pid, fpid; siginfo_t info; if((pid = fork()) < 0) err_sys("fork error."); else if(pid == 0) { sleep(5); _exit(0); } while((fpid = waitid(P_PID,pid,&info,WEXITED)) == 0) { printf("terminated process success exit,and process ID: %d.\n",pid); sleep(1); } exit(0); }该程序是在现有进程使用 fork 创建一个子进程,在 fork 返回的子进程中睡眠 5s 后正常退出;在 fork 返回的父进程调用 waitid 函数等待与子进程的进程 ID 与 pid 相等子进程退出状态,waiti 成功则返回0,打印一条信息;输出结果如下:
terminated process success exit,and process ID: 6589.
/* * wait3 和 wait4 函数 * 函数功能:等待一个进程终止; * 返回值:若成功则返回进程ID,若出错则返回-1; * 函数原型: */ #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);
《UNIX 高级环境编程》