WAIT - Linux手册页

时间:2019-08-20 17:59:37  来源:igfitidea点击:

Linux程序员手册 第2部分
更新日期: 2020-04-11

名称

wait,waitpid,waitid-等待进程更改状态

语法

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
/ *这是glibc和POSIX接口;看到
注意有关原始系统调用的信息。 * /

glibc的功能测试宏要求(请参阅feature_test_macros(7)):

waitid():

从glibc 2.26开始:_XOPEN_SOURCE>= 500 || _POSIX_C_SOURCE>= 200809L Glibc 2.25和更早版本: _XOPEN_SOURCE || / *自glibc 2.12起:* / _POSIX_C_SOURCE>= 200809L || / * Glibc版本

说明

所有这些系统调用都用于在调用过程的子级中等待状态更改,并获取有关状态已更改的子级的信息。状态更改被认为是:子项终止;这个孩子被一个信号挡住了;或该孩子被信号恢复。如果子项已终止,则执行等待可以使系统释放与子项关联的资源;如果不执行等待,则终止的子代将保持"僵尸"状态(请参见下面的"注意")。

如果孩子的状态已经改变,则这些调用将立即返回。否则,它们将阻塞,直到子级更改状态或信号处理程序中断该调用为止(假定使用sigaction(2)的SA_RESTART标志不会自动重新启动系统调用)。在此页面的其余部分中,状态已更改并且尚未被这些系统调用之一等待的孩子称为"等待"。

wait() and waitpid()

wait()系统调用将暂停调用线程的执行,直到其子级之一终止。调用wait(&wstatus)等效于:

waitpid(-1, &wstatus, 0);

waitpid()系统调用将暂停调用线程的执行,直到pid参数指定的子级更改状态为止。默认情况下,waitpid()仅等待终止的子级,但是可以通过options参数修改此行为,如下所述。

pid的值可以是:

< -1
表示等待任何进程组ID等于pid的绝对值的子进程。
-1
意思是等待任何子进程。
0
表示在调用waitpid()时等待进程组ID与调用进程的ID相等的任何子进程。
> 0
意思是等待进程ID等于pid值的子进程。

options的值是以下常量的零或更多的或:

WNOHANG
如果没有孩子退出,请立即返回。
WUNTRACED
如果孩子已经停止(但未通过ptrace(2)进行跟踪),它也返回。即使未指定此选项,也会提供已停止的跟踪子项的状态。
WCONTINUED(since Linux 2.6.10)
如果已通过交付SIGCONT恢复了被阻止的孩子,也将返回。

(对于仅Linux选项,请参见下文。)

如果wstatus不为NULL,则wait()和waitpid()将状态信息存储在它指向的int中。可以使用以下宏检查该整数(这些宏将整数本身作为参数,而不是指向它的指针,就像在wait()和waitpid()中一样!):

WIFEXITED(wstatus)
如果子项正常终止(即通过调用exit(3)或_exit(2)或从main()返回),则返回true。
WEXITSTATUS(wstatus)
返回孩子的退出状态。它由状态参数的最低有效8位组成,该状态参数是子级在对exit(3)或_exit(2)的调用中指定的,或作为main()中return语句的参数。仅当WIFEXITED返回true时,才应使用此宏。
WIFSIGNALED(wstatus)
如果子进程被信号终止,则返回true。
WTERMSIG(wstatus)
返回导致子进程终止的信号编号。仅当WIFSIGNALED返回true时,才应使用此宏。
WCOREDUMP(wstatus)
如果子项产生了核心转储,则返回true(请参阅core(5))。仅当WIFSIGNALED返回true时,才应使用此宏。
POSIX.1-2001中未指定此宏,并且在某些UNIX实现中(例如AIX,SunOS)不可用。因此,请将其用法包含在#ifdef WCOREDUMP ... #endif中。
WIFSTOPPED(wstatus)
如果子进程由于传递信号而停止,则返回true;否则返回true。仅当使用WUNTRACED进行调用或跟踪孩子时,才有可能(请参见ptrace(2))。
WSTOPSIG(wstatus)
返回导致孩子停止的信号编号。仅当WIFSTOPPED返回true时,才应使用此宏。
WIFCONTINUED(wstatus)
(从Linux 2.6.10开始)如果子进程通过SIGCONT的交付而恢复,则返回true。

waitid()

waitid()系统调用(自Linux 2.6.9起可用)提供了对要等待的子状态更改的更精确控制。

idtype和id参数选择要等待的子级,如下所示:

idtype== P_PID
等待其进程ID与ID匹配的孩子。
idtype== P_PIDFD(since Linux 5.4)
等待id中指定的PID文件描述符所引用的子级。 (有关PID文件描述符的更多信息,请参见pidfd_open(2)。)
idtype== P_PGID
等待进程组ID与ID匹配的任何子级。从Linux 5.4开始,如果id为零,则在调用时等待与调用者的进程组在同一进程组中的任何子级。
idtype== P_ALL
等待任何孩子; id被忽略。

通过对选项中的以下一个或多个标志进行"或"运算来指定要等待的子状态更改:

WEXITED
等待终止的孩子。
WSTOPPED
等待被信号传递阻止的儿童。
WCONTINUED
等待(先前已停止)通过SIGCONT交付已恢复的孩子。

以下标志还可以在选项中进行"或"运算:

WNOHANG
至于waitpid()。
WNOWAIT
让孩子处于等待状态;以后的等待调用可用于再次检索子状态信息。

成功返回后,waitid()填充infop指向的siginfo_t结构的以下字段:

si_pid
子进程的ID。
si_uid
子级的真实用户标识。 (在大多数其他实现中未设置此字段。)
si_signo
始终设置为SIGCHLD。
si_status
_exit(2)(或exit(3))给出的孩子的退出状态,或者导致孩子终止,停止或继续的信号。 si_code字段可用于确定如何解释此字段。
si_code
设置为以下之一:CLD_EXITED(称为_exit(2)的子代); CLD_KILLED(被信号杀死的孩子); CLD_DUMPED(被信号杀死的孩子,并丢弃核心); CLD_STOPPED(子进程被信号停止); CLD_TRAPPED(跟踪的孩子已被困);或CLD_CONTINUED(由SIGCONT继承的子代)。

如果在options中指定了WNOHANG并且没有子级处于等待状态,则waitid()立即返回0,并且infop指向的siginfo_t结构的状态取决于实现。要(适当地)将此情况与孩子处于等待状态的情况区分开来,请在调用之前将si_pid字段清零,并在调用返回后检查此字段中的非零值。

POSIX.1-2008技术更正1(2013)添加了一项要求,即在选项中指定WNOHANG且没有子级处于可等待状态时,waitid()应将结构的si_pid和si_signo字段清零。在满足此要求的Linux和其他实现上,在调用waitid()之前不必将si_pid字段清零。但是,在这一点上,并非所有实现都遵循POSIX.1规范。

返回值

wait():成功后,返回终止子进程的进程号;如果出错,则返回-1。

waitpid():成功后,返回状态已更改的子进程的ID;如果指定了WNOHANG并且存在由pid指定的一个或多个子代,但尚未更改状态,则返回0。出错时,返回-1。

waitid():成功返回0,或者如果指定了WNOHANG并且id所指定的子代还没有更改状态,则返回0;如果出错,则返回-1。

在发生错误的情况下,每个调用都会将errno设置为适当的值。

错误说明

ECHILD
(对于wait())调用过程没有任何未等待的子代。
ECHILD
(用于waitpid()或waitid())由pid(waitpid())或idtype和id(waitid())指定的进程不存在,或者不是调用进程的子进程。 (如果将SIGCHLD的操作设置为SIG_IGN,这可能会发生在自己的孩子身上。另请参见关于线程的Linux Notes部分。)
EINTR
未设置WNOHANG,并且捕获到无阻塞信号或SIGCHLD;参见signal(7)。
EINVAL
options参数无效。

遵循规范

SVr4、4.3BSD,POSIX.1-2001。

备注

一个终止但尚未等待的孩子成为"僵尸"。内核维护有关僵尸进程的最少信息集(PID,终止状态,资源使用信息),以便允许父级稍后执行等待以获取有关子级信息的等待。只要没有通过等待将僵尸从系统中删除,僵尸就会占用内核进程表中的一个插槽,如果该表已满,将无法创建更多进程。如果父进程终止,则init(1)(或通过使用prctl(2)PR_SET_CHILD_SUBREAPER操作定义的最近的" subreaper"进程)采用其"僵尸"子级(如果有)。 init(1)自动执行等待以删除僵尸。

POSIX.1-2001指定如果将SIGCHLD的处置设置为SIG_IGN或为SIGCHLD设置SA_NOCLDWAIT标志(请参阅sigaction(2)),则终止的子代不会成为僵尸,并且对wait()或waitpid( )将阻塞,直到所有子级都终止,然后将errno设置为ECHILD失败。 (原始POSIX标准未指定将SIGCHLD设置为SIG_IGN的行为。请注意,即使SIGCHLD的默认设置为"忽略",将设置显式设置为SIG_IGN也会导致对僵尸进程子项的不同处理。)

Linux 2.6符合POSIX要求。但是,Linux 2.4(及更早版本)没有:如果在忽略SIGCHLD的同时进行了wait()或waitpid()调用,则该调用的行为就像未忽略SIGCHLD一样,也就是说,该调用将阻塞直到下一个子进程终止,然后返回该子进程的进程ID和状态。

Linux notes

在Linux内核中,内核调度的线程不是进程的独特构造。相反,线程只是使用Linux独特的clone(2)系统调用创建的进程。其他例程(例如可移植pthread_create(3)调用)是使用clone(2)实现的。在Linux 2.4之前,线程只是进程的一种特殊情况,结果,一个线程无法等待另一个线程的子代,即使后者属于同一线程组。但是,POSIX规定了此类功能,并且由于Linux 2.4,线程可以并且在默认情况下将等待同一线程组中其他线程的子级。

以下Linux特定选项适用于使用clone(2)创建的子代;从Linux 4.7开始,它们还可以与waitid()一起使用:

__WCLONE
仅等待"克隆"孩子。如果省略,则仅等待"非克隆"孩子。 ("克隆"子项是在终止时不向其父项传递任何信号或SIGCHLD以外的信号的子级。)如果还指定了__WALL,则忽略此选项。
__WALL(since Linux 2.4)
等待所有子项,而不管类型("克隆"还是"非克隆")。
__WNOTHREAD(since Linux 2.4)
不要等待同一线程组中其他线程的子级。这是Linux 2.4之前的默认设置。

从Linux 4.7开始,如果正在跟踪子级,则将自动隐含__WALL标志。

C library/kernel differences

wait()实际上是一个库函数(在glibc中)被实现为对wait4(2)的调用。

在某些体系结构上,没有waitpid()系统调用。相反,此接口是通过C库包装函数来实现的,该函数调用wait4(2)。

rawwaitid()系统调用采用第五个参数,类型为struct rusage *。如果此参数为非NULL,则以与wait4(2)相同的方式,用于返回有关子级的资源使用信息。有关详细信息,请参见getrusage(2)。

BUGS

根据POSIX.1-2008,调用waitid()的应用程序必须确保infop指向siginfo_t结构(即,它是一个非空指针)。在Linux上,如果infop为NULL,则waitid()成功,并返回等待的子进程的进程ID。应用程序应避免依赖于此不一致,非标准和不必要的功能。

示例

以下程序演示了fork(2)和waitpid()的用法。该程序将创建一个子进程。如果没有向程序提供命令行参数,则子进程将使用pause(2)暂停执行,以允许用户向子进程发送信号。否则,如果提供了命令行参数,则子级将立即退出,使用命令行上提供的整数作为退出状态。父进程执行一个循环,使用waitpid()监视子进程,并使用上述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
$

Program source

#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int
main(int argc, char *argv[])
{
    pid_t cpid, w;
    int wstatus;

    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, &wstatus, WUNTRACED | WCONTINUED);
            if (w == -1) {
                perror("waitpid");
                exit(EXIT_FAILURE);
            }

            if (WIFEXITED(wstatus)) {
                printf("exited, status=%d\n", WEXITSTATUS(wstatus));
            } else if (WIFSIGNALED(wstatus)) {
                printf("killed by signal %d\n", WTERMSIG(wstatus));
            } else if (WIFSTOPPED(wstatus)) {
                printf("stopped by signal %d\n", WSTOPSIG(wstatus));
            } else if (WIFCONTINUED(wstatus)) {
                printf("continued\n");
            }
        } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
        exit(EXIT_SUCCESS);
    }
}

另外参见

_exit(2),clone(2),fork(2),kill(2),ptrace(2),sigaction(2),signal(2),wait4(2),pthread_create(3),core(5),凭证(7),信号(7)

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/