waitpid() - 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 的选项,请参见下文。)仅当 **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**() 或作为 main() 中 return 语句的参数时指定的 *status* 参数的最低有效 16-8 位组成。只有当 **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、终止状态、资源使用信息),以便父进程稍后执行等待操作以获取有关子进程的信息。
只要僵尸进程没有通过等待操作从系统中移除,它就会占用内核进程表中的一个槽位,如果该表填满,则将无法创建更多进程。如果父进程终止,则其“僵尸”子进程(如果有)将由**init**(8) 采用,**init**(8) 会自动执行等待操作以移除僵尸进程。
POSIX.1-2001 规定,如果**SIGCHLD** 的处理方式设置为**SIG_IGN** 或为**SIGCHLD** 设置了**SA_NOCLDWAIT** 标志(参见**sigaction**(2)),则终止的子进程不会变成僵尸进程,并且对**wait**() 或**waitpid**() 的调用将阻塞,直到所有子进程都终止,然后以**ECHILD** 设置的*errno* 失败。(原始 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。
参见
广告
|