wait() - 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的值是以下常量中的一个或多个的 OR
|
标签 | 描述 |
WNOHANG | 如果没有任何子进程退出,则立即返回。 |
WUNTRACED | 如果子进程已停止(但未通过ptrace(2)跟踪),则也返回。即使未指定此选项,也会提供已停止的跟踪子进程的状态。 |
WCONTINUED | (自 Linux 2.6.10 起)如果已停止的子进程已通过传递SIGCONT恢复,则也返回。 |
|
(对于仅限 Linux 的选项,请参见下文。)WUNTRACED和WCONTINUED选项仅在SIGCHLD信号未设置SA_NOCLDSTOP标志时有效(请参见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中对以下标志中的一个或多个进行 OR 来指定 |
WEXITED | 等待已终止的子进程。 |
WSTOPPED | 等待因信号传递而停止的子进程。 |
WCONTINUED | 等待(先前已停止的)已通过传递SIGCONT恢复的子进程。 |
以下标志还可以与options进行 OR |
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():成功时或如果指定了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 | 仅等待“克隆”子进程。如果省略,则仅等待“非克隆”子进程。(“克隆”子进程是指在终止时不向其父进程传递信号或传递除 **SIGCHLD** 之外的信号的子进程。)如果也指定了 **__WALL**,则忽略此选项。 |
__WALL | (自 Linux 2.4 起)等待所有子进程,无论类型(“克隆”或“非克隆”)。 |
__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。
另请参见
广告
|