wait,waitpid,waitid,wait3,wait4函数详解

对于许多需要创建子进程的应用来说,父进程能够监测子进程的终止时间和过程是很有必要的,wait以及相关系统调用函数提供了这一功能

wait函数

1
2
#include<sys/wait.h>
pid_t wait(int *status); //成功返回子进程id,出错返回-1

系统调用wait执行如下动作:

  • 如果调用进程并无之前未被等待的子进程终止,调用将一直阻塞,直至某个子进程终止。如果调用时已有子进程终止,wait则立即返回。
  • 如果status非空,那么关于子进程的如何终止的信息则会通过status指向的正序变量返回。
  • 内核将会为父进程下所有子进程的运行总量追加进程CPU时间以及资源使用。
  • 将终止子进程的id作为wait的结果返回。
    可能出错的原因之一是调用进程并没有之前未被等待的子进程。此时会将errno置为ECHILD。

可以这样来等待调用进程的所有子进程终止:

1
2
3
4
while((childid = wait(NULL)) != -1)
continue;
if(errno == ECHILD)
exit(0);

waitpid函数

系统调用wait存在诸多限制,而waitpid则突破了这些限制:

  • 如果父进程已经创建了多个子进程,使用wait将无法等待某个特定子进程的完成,只能按顺序等待下一个子进程的终止。
  • 如果没有子进程退出,wait将一直保持阻塞,有时希望执行非阻塞的等待:是否有子进程退出,立判可知。
  • 使用wait只能发现那些已经终止的子进程。子进程因某个信号而停止,或是已停止的信号因收到SIGCONT信号后恢复执行的情况无法知晓。(停止与终止不同,停止即暂停、挂起,终止即over)
1
2
#include<sys/wait.h>
pid_t waitpid(pid_t pid,int *status,int options); //成功返回子进程id,出错返回0或-1

status与wait中的意义相同。参数pid用来表示需要等待的具体子进程:

  • 如果pid大于0,表示等待进程id为pid的子进程。
  • 如果pid等于0,表示等待与调用进程同一个进程组的所有子进程。
  • 如果pid小于-1,则会等待进程组标识符与pid的绝对值相等的所有子进程。
  • 如果pid等于-1,则会等待任意子进程,等价于wait(&status)。

参数options是一个位掩码,可以包含0个或多个标志:
WUNTRACED:除了返回终止子进程的信息外,还返回因信号而停止的子进程信息。
WCONTINUED:返回那些因收到SIGCONT信号而恢复执行的已停止子进程的状态信息。
WNOHANG:如果参数pid所指定的子进程并未发生状态变化,则立即返回不会阻塞。这种情况下waitpid返回0。

waitid函数

与waitpid类似,waitid返回子进程的状态,不过waitid提供了waitpid所没有的扩展功能。

1
2
3
#include<sys/wait.h>
int waitid(idtype_t idtype,id_t id,siginfo_t *infop,int options);
//成功或者WNOHANG时无子进程改变时返回0,出错返回-1

参数idtype和id指定需要等待哪些子进程:

  • 如果idtype为P_ALL,则等待任何子进程,同时忽略id值。
  • 如果idtype为P_PID,则等待进程id为id进程的子进程。
  • 如果idtype为P_PGID,则等待进程组id为id的所有子进程。

注:与waitpid不同,不能靠指定id为0来表示与调用者属于同一进程租的所有进程,必须以getpgrp函数的返回值来显式指定调用者的进程组id
waitpid与waitid最显著的区别在于,对于应该等待的子进程事件,waitid可以更为精确的控制。可以通过在options中指定一个或多个标识来实现控制:
WEXITED :等待已终止的子进程,无论其是否正常返回。
WSTOPPED:等待已通过信号而停止的子进程。
WCONTINUED : 等待经由信号SIGCONT而恢复的子进程。
WNOHANG:如果匹配id值的子进程中无状态信息需要返回,则立即返回而不阻塞。
WNOWAIT:会返回子进程状态,但是子进程依然处于可等待状态,稍后可以再次等待并获取相同的信息。
执行成功返回0,并更新指针infop所指向的siginfo_t结构,其包含子进程的相关信息。

wait3和wait4函数

wait3和wait4函数执行与waitpid类似的工作,主要的语义差别在于,wait3和wait4在参数rusage所指向的结构中返回终止子进程的资源使用情况。其中包括进程使用的CPU时间总量以及内存管理的统计数据。

1
2
3
4
5
6
#define _BSD_SOURCE
#include<sys/resource.h>
#include<sys/wait.h>
pid_t wait3(int *status,int options,struct resage *rusage);
pid_t wait4(pid_t pid,int *status,int options,struct rusage *rusage);
//成功返回子进程id,出错返回-1

除了对参数rusage的使用外,调用wait3等价于:waitpid(-1,&status,options);
类似的wait4等价于:waitpid(pid,&status,options);
也就是说,wait3等待的是任意子进程,而wait4等待的是选定的一个或多个子进程。
但是其实wait3和wait4的返回值没什么价值,此外这两函数的移植性比较差。尽量不要用…


参考资料:
《linux/UNIX系统编程手册 上》(tlpi)
《UNIX环境高级编程 3th》(APUE)