WAIT(2) WAIT(2) NAME wait, waitpid, wait3 - wait for child processes to stop or terminate C SYNOPSIS #include <sys/types.h> #include <sys/wait.h> pid_t wait (int *statptr); pid_t waitpid (pid_t pid, int *statptr, int options); #include <sys/types.h> #include <sys/wait.h> #include <sys/resource.h> pid_t wait3 (int *statptr, int options, struct rusage *rusage); DESCRIPTION wait suspends the calling process until one of the immediate children terminate, or until a child that is being traced stops because it has hit an event of interest. The wait will return prematurely if a signal is received. If all child processes stopped or terminated prior to the call on wait, return is immediate. If the call is successful, the process ID of a child is returned. wait3 is BSD's extension of wait. It provides an alternate interface for programs that must not block when collecting the status of child processes. waitpid is POSIX's extension of wait. The pid argument specifies a set of child processes for which status is requested. waitpid only returns the status of a child process from this set. PARAMETERS statptr (all functions): If statptr is non-zero, 16 bits of information called status are stored in the low-order 16 bits of the location pointed to by statptr. Status may be evaluated with the macros described on wstat(5). Status can be used to differentiate between stopped and terminated child processes. If the child process terminated, status identifies the cause of termination and passes useful information to the parent. status is interpreted as follows: If the child process stopped, the predicate WIFSTOPPED(*statptr) will evaluate to non-zero and WSTOPSIG(*statptr) will return the signal number that caused the process to stop. (The high-order 8 bits of status will contain the signal number and the low-order 8 bits are set equal to WSTOPFLG.) If the child process terminated due to an exit call, the predicate WIFEXITED(*statptr) will evaluate to non-zero, and WEXITSTATUS(*statptr) will return the argument that the child process passed to _exit or exit, or the value the child process returned from main [see exit(2)]. (The low-order 8 bits of status will be zero and the high-order 8 bits will contain the low-order 8 bits of the argument that the child process passed to exit.) If the child process terminated due to a signal, the predicate WIFSIGNALED(*statptr) will evaluate to non-zero, and WTERMSIG(*statptr) will return the signal number that caused the termination. (The high-order 8 bits of status will be zero and the low-order 8 bits will contain the number of the signal.) In addition, if WCOREFLG is set, a ``core image'' will have been produced [see signal(2)]. rusage (wait3): If rusage non-zero, a summary of the resources used by the terminated process and all its children is returned (this information is currently not available for stopped processes). pid (waitpid): 1) If pid is equal to -1, status is requested for any child process. In this respect, waitpid is then equivalent to wait. 2) If pid is greater than zero, it specifies the process ID of a single child process for which status is requested. 3) If pid is equal to zero, status is requested for any child process whose process group ID is equal to that of the calling process. 4) If pid is less than -1, status is requested for any child process whose process group ID is equal to the absolute value of pid. options (waitpid and wait3): The options argument is constructed from the bitwise inclusive OR of zero or more of the following flags, defined in the header <sys/wait.h>: WNOHANG The function will not suspend execution of the calling process if status is not immediately available for one of the child processes. WUNTRACED The status of child processes that are stopped due to a SIGTTIN, SIGTTOU, SIGTSTP, or SIGSTOP signal, and whose status has not yet been reported since they stopped, are reported to the requesting process. If a parent process terminates without waiting for its child processes to terminate, the parent process ID of each child process is set to 1. This means the initialization process inherits the child processes [see intro(2)]. SIGCLD HANDLING IRIX has three distinct version of signal routines: System V (signal(2) and sigset(2)), 4.3BSD (signal(3B) and sigvec(3B)), and POSIX (sigaction(2)). Each version has a method by which a parent can be certain that it waits on all of its children even if they are executing concurrently. In each version, the parent installs a signal handler for SIGCLD to wait for its children, but the specific code differs in subtle, albeit vital, ways. Sample programs below are used to illustrate each of the three methods. Note that System V refers to this signal as SIGCLD, whereas BSD calls it SIGCHLD. For compatibility with both systems they are defined to be the same signal number, and may therefore be used interchangeably. System V: System V's SIGCLD mechanism guarantees that no SIGCLD signals will be lost. It accomplishes this by forcing the process to reinstall the handler (via signal or sigset calls) when leaving the handler. Note that whereas signal(2) sets the signal disposition back to SIG_DFL each time the handler is called, sigset(2) keeps it installed, so SIGCLD is the only signal that demands this reinstallation, and that only because the installation call allows the kernel to check for additional instances of the signal that occurred while the process was executing in the handler. The code below is the System V example. Note that the sigpause(2) creates a window during which SIGCLD is not blocked, allowing the parent to enter its handler. /* * System V example of wait-in-SIGCLD-handler usage */ #include <signal.h> #include <stdio.h> #include <sys/wait.h> static void handler(int); #define NUMKIDS 4 volatile int kids = NUMKIDS; main() { int i, pid; sigset(SIGCLD, handler); sighold(SIGCLD); for (i = 0; i < NUMKIDS; i++) { if (fork() == 0) { printf("Child %d\n", getpid()); exit(0); } } while (kids > 0) { sigpause(SIGCLD); sighold(SIGCLD); } } static void handler(int sig) { int pid, status; printf("Parent (%d) in handler, ", getpid()); pid = wait(&status); kids--; printf("child %d, now %d left\n", pid, kids); /* * Now reinstall handler & cause SIGCLD to be re-raised * if any more children exited while we were in here. */ sigset(SIGCLD, handler); } BSD: 4.3BSD solved this problem differently: instead of guaranteeing that no SIGCHLD signals are lost, it provides a WNOHANG option to wait3 that allows parent processes to do non-blocking waits in loops, until no more stopped or zombied children exist. Note that the handler must be able to deal with the case in which no applicable children exist; if one or more children exit while the parent is in the handler, all may get reaped, yet if one or more SIGCHLD signals arrived while the parent was in its handler, the signal will remain pending, the parent will reenter the handler, and the wait3 call will return 0. Note that it is not necessary to call sigvec upon exit from the handler. /* * BSD example of wait3-in-SIGCHLD handler usage */ #define _BSD_SIGNALS #include <signal.h> #include <stdio.h> #include <sys/wait.h> static int handler(int); #define NUMKIDS 4 volatile int kids = NUMKIDS; main() { int i, pid; struct sigvec vec; vec.sv_handler = handler; vec.sv_mask = sigmask(SIGCHLD); vec.sv_flags = 0; sigvec(SIGCHLD, &vec, NULL); sigsetmask(sigmask(SIGCHLD)); for (i = 0; i < NUMKIDS; i++) { if (fork() == 0) { printf("Child %d\n", getpid()); exit(0); } } while (kids > 0) { sigpause(0); } } static int handler(int sig) { int pid; int status; printf("Parent (%d) in handler, ", getpid()); while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { kids--; printf("child %d, now %d left\n", pid, kids); } } POSIX: POSIX improved on the BSD method by providing waitpid, that allows a parent to wait on a particular child process if desired. In addition, the IRIX implementation of sigaction(2) checks for zombied children upon exit from the system call if the specified signal was SIGCLD and the disposition of the signal handling was changed. If zombied children exist, another SIGCLD is raised. This solves the problem that occurs when a parent creates children, but a module that it links with (typically a libc routine such as system(3)) creates and waits on its own children. Two problems have classically arisen in such a scheme: 1) until the advent of waitpid, the called routine could not specify which children to wait on; it therefore looped, waiting and discarding children until the one (or ones) it had created terminated, and 2) if the called routine changed the disposition of SIGCLD and then restored the previous handler upon exit, children of the parent (calling) process that had terminated while the called routine executed would be missed in the parent, because the called routine's SIGCLD handler would reap and discard those children. The addition of waitpid and the IRIX implementation of sigaction solves both of these problems. Note that neither the BSD nor the System V signal routines on IRIX have these properties, in the interests of compatibility. WARNING: programs that install SIGCLD handlers that set flags instead of executing waitpids and then attempt to restore the previous signal handler (via sigaction) upon return from the handler will create infinite loops. /* * POSIX example of waitpid-in-SIGCHLD handler usage */ #include <signal.h> #include <stdio.h> #include <sys/wait.h> static void handler(int); #define NUMKIDS 4 volatile int kids = NUMKIDS; /* * If waitpid's 1st argument is -1, it waits for any child. */ #define ANYKID -1 main() { int i; pid_t pid; struct sigaction act; sigset_t set, emptyset; act.sa_handler = handler; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGCHLD); act.sa_flags = 0; sigaction(SIGCHLD, &act, NULL); sigemptyset(&set); sigemptyset(&emptyset); sigaddset(&set, SIGCHLD); sigprocmask(SIG_BLOCK, &set, NULL); setbuf(stdout, NULL); for (i = 0; i < NUMKIDS; i++) { if (fork() == 0) { printf("Child %d\n", getpid()); exit(0); } } while (kids > 0) { sigsuspend(&emptyset); } } static void handler(int sig) { pid_t pid; int status; printf("Parent (%d) in handler, ", getpid()); pid = waitpid(ANYKID, &status, WNOHANG); while (pid > 0) { kids--; printf("child %d, now %d left\n", pid, kids); pid = waitpid(ANYKID, &status, WNOHANG); } } DIAGNOSTICS wait fails and its actions are undefined if statptr points to an invalid address. If wait, wait3, or waitpid return due to a stopped or terminated child process, the process ID of the child is returned to the calling process. wait3 and waitpid return 0 if WNOHANG is specified and there are currently no stopped or exited children (although children DO exist). Otherwise, a value of -1 is returned and errno is set to indicate the error: [EINTR] The calling process received a signal. [ECHILD] The calling process has no existing unwaited-for child processes. [ECHILD] The process or process group specified by pid does not exist or is not a child of the calling process (waitpid only). [EFAULT] The rusage or statptr arguments (where applicable) point to illegal addresses. [EINVAL] The value of the options argument is not valid (waitpid and wait3 only). SEE ALSO exec(2), exit(2), fork(2), intro(2), pause(2), ptrace(2), signal(2), sigset(2), sigpause(2), sigaction(2), sigsuspend(2), sigprocmask(2), signal(3B), sigvec(3B), sigpause(3B), wait(3b), getrusage(3), wstat(5). NOTE Currently, wait3 returns zero for the ru_ixrss, ru_idrss and ru_isrss fields in rusage. Page 7