wait 函数和 waitpid 函数
当一个进程正常或异常终止时,内核会向其父进程发送 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 有一个选项 option,根据选项的不同值可使其调用者不发生阻塞。
- waitpid 并不一定要等待在其调用后的第一个子进程终止,根据不同选项,可以控制自己感兴趣要等待的进程。
- waitpid 可以等待一个特定的子进程,而wait 则返回任一终止子进程的状态;
- waitpid 提供一个非阻塞的 wait 版本;
- waitpid 支持作业控制;
可以用以下宏查看进程的终止状态:
/* 检查 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
waitid 函数
该函数类似于上面的 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 函数
函数 wait3 和 wait4 提供的功能比函数 wait,waitpid 和 waitid 所提供的功能要多一个,这与附加参数 rusage 有关,该参数要求内核返回由终止进程及其所有子进程使用的资源汇总。/*
* 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 高级环境编程》