waitid() - Unix, Linux系统调用
广告
名称
wait, waitpid - 等待进程状态改变
概要
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
int waitid(idtype_t idtype, id_t id, siginfo_t * infop , int options );
|
描述
所有这些系统调用都用于等待调用进程子进程的状态更改,并获取其状态已更改的子进程的信息。状态更改被认为是:子进程终止;子进程被信号停止;或子进程被信号恢复。对于终止的子进程,执行 wait 允许系统释放与子进程关联的资源;如果没有执行 wait,则终止的子进程将保持“僵尸”状态(参见下面的注释)。
如果子进程的状态已经改变,则这些调用会立即返回。否则,它们会阻塞,直到子进程的状态改变或信号处理程序中断调用(假设系统调用没有使用sigaction(2)的SA_RESTART标志自动重启)。在本页的其余部分,状态已更改且尚未被这些系统调用之一等待的子进程被称为可等待的。
wait() 和 waitpid()
wait() 系统调用挂起当前进程的执行,直到其一个子进程终止。调用wait(&status)等效于
waitpid() 系统调用挂起当前进程的执行,直到由pid参数指定的子进程状态更改。默认情况下,waitpid() 仅等待终止的子进程,但这可以通过下面的options参数进行修改。
pid 的值可以是
| 标签 | 描述 |
| < -1 | 表示等待任何进程组 ID 等于pid绝对值的子进程。 |
| -1 | 表示等待任何子进程。 |
| 0 | 表示等待任何进程组 ID 等于调用进程的进程组 ID 的子进程。 |
| > 0 | 表示等待进程 ID 等于pid值的子进程。 |
options 的值是以下常量的或运算结果,可以为零个或多个
|
|
| 标签 | 描述 |
|
WNOHANG | 如果没有子进程退出,则立即返回。 |
| WUNTRACED | 如果子进程已停止(但未通过ptrace(2)跟踪),则也返回。即使未指定此选项,也会提供已停止的跟踪子进程的状态。 |
| WCONTINUED | (自 Linux 2.6.10 起)如果已停止的子进程已通过传递SIGCONT恢复,则也返回。 |
|
(对于仅限 Linux 的选项,请参见下文。)只有在SIGCHLD信号的SA_NOCLDSTOP标志未设置的情况下,WUNTRACED和WCONTINUED选项才有效(参见sigaction(2))。
如果status不为 NULL,wait() 和 waitpid() 会将状态信息存储到它指向的int中。可以使用以下宏检查此整数(这些宏采用整数本身作为参数,而不是指向它的指针,如在wait() 和 waitpid() 中所做的那样!)
| 标签 | 描述 |
| WIFEXITED(status) | 如果子进程正常终止,则返回 true,即通过调用exit(3) 或 _exit(2),或从 main() 返回。 |
| WEXITSTATUS(status) | 返回子进程的退出状态。这包括子进程在调用exit() 或 _exit() 时指定的status参数的最低有效 16-8 位,或作为 main() 中 return 语句的参数。只有在WIFEXITED返回 true 时才应使用此宏。 |
|
WIFSIGNALED(status) | 如果子进程被信号终止,则返回 true。 |
|
WTERMSIG(status) | 返回导致子进程终止的信号编号。只有在WIFSIGNALED返回 true 时才应使用此宏。 |
|
WCOREDUMP(status) | 如果子进程产生了核心转储,则返回 true。只有在WIFSIGNALED返回 true 时才应使用此宏。此宏未在 POSIX.1-2001 中指定,并且在某些 Unix 实现(例如,AIX、SunOS)上不可用。仅在 #ifdef WCOREDUMP ... #endif 中使用。 |
|
WIFSTOPPED(status) | 如果子进程被信号停止,则返回 true;这只有在使用WUNTRACED调用或正在跟踪子进程(参见ptrace(2))时才有可能。 |
|
WSTOPSIG(status) | 返回导致子进程停止的信号编号。只有在WIFSTOPPED返回 true 时才应使用此宏。 |
|
WIFCONTINUED(status) | (自 Linux 2.6.10 起)如果子进程已通过传递SIGCONT恢复,则返回 true。 |
waitid()
waitid() 系统调用(自 Linux 2.6.9 起可用)提供了对要等待哪些子进程状态更改的更精确控制。
idtype 和 id 参数选择要等待的子进程,如下所示
| 标签 | 描述 |
|
idtype == P_PID | 等待进程 ID 与id匹配的子进程。 |
|
idtype == P_PGID | 等待任何进程组 ID 与id匹配的子进程。 |
|
idtype == P_ALL | 等待任何子进程;忽略id。 |
| 要等待的子进程状态更改通过在options中对以下一个或多个标志进行或运算来指定 |
|
WEXITED | 等待已终止的子进程。 |
|
WSTOPPED | 等待已被信号停止的子进程。 |
|
WCONTINUED | 等待(先前已停止的)已通过传递SIGCONT恢复的子进程。 |
| 此外,可以在options中对以下标志进行或运算 |
|
WNOHANG | 与waitpid()相同。 |
|
WNOWAIT | 将子进程保留在可等待状态;以后的 wait 调用可再次用于检索子进程状态信息。 |
| 成功返回后,waitid() 将填充infop指向的siginfo_t结构的以下字段 |
|
si_pid
| 子进程的进程 ID。 |
|
si_uid
| 子进程的真实用户 ID。(此字段在大多数其他实现中未设置。) |
|
si_signo
| 始终设置为SIGCHLD。 |
|
si_status
| 子进程的退出状态(如传递给_exit(2)(或exit(3))),或导致子进程终止、停止或继续的信号。si_code字段可用于确定如何解释此字段。 |
|
si_code
| 设置为以下之一:CLD_EXITED(子进程调用_exit(2));CLD_KILLED(子进程被信号杀死);CLD_STOPPED(子进程被信号停止);或CLD_CONTINUED(子进程被SIGCONT继续)。 |
如果在options中指定了WNOHANG并且没有子进程处于可等待状态,则waitid() 会立即返回 0,并且infop指向的siginfo_t结构的状态未指定。为了区分这种情况与子进程处于可等待状态的情况,在调用之前将si_pid字段清零,并在调用返回后检查此字段中的非零值。
返回值
wait():成功时,返回终止的子进程的进程 ID;错误时,返回 -1。
waitpid():成功时,返回状态已更改的子进程的进程 ID;错误时,返回 -1;如果指定了WNOHANG并且pid指定的子进程尚未更改状态,则返回 0。
waitid():成功时返回 0,或者如果指定了WNOHANG并且id指定的子进程尚未更改状态,则返回 0;错误时,返回 -1。
在错误的情况下,这些调用中的每一个都会将errno设置为适当的值。
错误
| 标签 | 描述 |
|
ECHILD | (对于wait())调用进程没有任何未等待的子进程。 |
|
ECHILD | (对于waitpid() 或 waitid())由pid(waitpid())或idtype和id(waitid())指定的进程不存在,或者不是调用进程的子进程。(如果 SIGCHLD 的操作设置为 SIG_IGN,则对于自身的子进程也可能发生这种情况。另请参见关于线程的 LINUX 说明部分。) |
|
EINTR |
未设置WNOHANG,并且捕获了未阻塞的信号或SIGCHLD。 |
|
EINVAL | options参数无效。 |
注释
终止但尚未等待的子进程成为“僵尸”。内核维护关于僵尸进程的一组最小信息(PID、终止状态、资源使用信息),以便父进程稍后执行 wait 以获取有关子进程的信息。
只要僵尸进程没有通过 wait 从系统中删除,它就会消耗内核进程表中的一个槽,如果此表填满,则将无法创建更多进程。如果父进程终止,则其“僵尸”子进程(如果有)将由init(8) 采用,后者会自动执行 wait 以删除僵尸进程。
POSIX.1-2001 规定,如果将SIGCHLD的处理方式设置为SIG_IGN,或者为SIGCHLD设置了SA_NOCLDWAIT标志(参见sigaction(2)),那么终止的子进程不会变成僵尸进程,而对wait() 或waitpid() 的调用将阻塞,直到所有子进程都终止,然后失败并设置errno为ECHILD。(最初的 POSIX 标准未指定将SIGCHLD设置为SIG_IGN的行为。)Linux 2.6 符合此规范。但是,Linux 2.4(及更早版本)不符合:如果在忽略SIGCHLD信号时进行wait() 或waitpid() 调用,则该调用的行为就像没有忽略SIGCHLD一样,也就是说,该调用会阻塞,直到下一个子进程终止,然后返回该子进程的进程 ID 和状态。
LINUX 注释
在 Linux 内核中,内核调度线程并非与进程不同的构造。相反,线程只是使用 Linux 独有的clone(2) 系统调用创建的进程;其他例程(例如可移植的pthread_create(3) 调用)是使用clone(2) 实现的。 在 Linux 2.4 之前,线程只是进程的一种特殊情况,因此一个线程无法等待另一个线程的子进程,即使后者属于同一个线程组。但是,POSIX 规定了此功能,并且从 Linux 2.4 开始,线程可以并且默认情况下会等待同一线程组中其他线程的子进程。
以下 Linux 特定的选项用于与使用clone(2) 创建的子进程一起使用;它们不能与waitid()一起使用。
| 标签 | 描述 |
|
__WCLONE | 仅等待“clone”子进程。如果省略,则仅等待“非clone”子进程。(“clone”子进程是指在终止时不会向其父进程传递信号或传递SIGCHLD以外的信号的子进程。)如果也指定了__WALL,则忽略此选项。 |
|
__WALL | (从 Linux 2.4 开始)等待所有子进程,无论类型(“clone”或“非clone”)。 |
|
__WNOTHREAD | (从 Linux 2.4 开始)不等待同一线程组中其他线程的子进程。在 Linux 2.4 之前,这是默认设置。 |
示例
以下程序演示了fork(2) 和waitpid(2) 的用法。该程序创建一个子进程。如果未向程序提供命令行参数,则子进程使用pause(2) 挂起其执行,以允许用户向子进程发送信号。否则,如果提供了命令行参数,则子进程会立即退出,并使用命令行上提供的整数作为退出状态。父进程执行一个循环,使用waitpid(2) 监控子进程,并使用上面描述的 W*() 宏来分析等待状态值。
以下 shell 会话演示了程序的用法。
$ ./a.out &
Child PID is 32360
[1] 32359
$ kill -STOP 32360
stopped by signal 19
$ kill -CONT 32360
continued
$ kill -TERM 32360
killed by signal 15
[1]+ Done ./a.out
$
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
pid_t cpid, w;
int status;
cpid = fork();
if (cpid == -1) { perror("fork"); exit(EXIT_FAILURE); }
if (cpid == 0) { /* Code executed by child */
printf("Child PID is %ld\n", (long) getpid());
if (argc == 1)
pause(); /* Wait for signals */
_exit(atoi(argv[1]));
} else { /* Code executed by parent */
do {
w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
if (w == -1) { perror("waitpid"); exit(EXIT_FAILURE); }
if (WIFEXITED(status)) {
printf("exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
exit(EXIT_SUCCESS);
}
}
|
符合标准
SVr4, 4.3BSD, POSIX.1-2001。
另请参见
广告
|