简单的进程间通信模型
关键字: fork, pipe, dup2, signal, SIGCHLD
20180820 tjy
转载请注明出处
先上代码,后面有解释。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
int entry_fun()
{
int pipefd[2];
int cpid;
int ret = 1;
char buffer[1024];
char * argv[2] = {"/bin/ls", NULL};
char * cmd = "/bin/ls";
//call pipe before fork a child process
ret = pipe(pipefd);
if(ret < 0)
{
printf("pipe failed.\n");
return ret;
}
cpid = fork();
if(cpid < 0)
{
printf("fork a process failed.\n");
return ret;
}
else if(cpid == 0)
{
//child process
//close pipefd[0], which means read fd from parent process
//we do not read from parent, so close this fd.
close(pipefd[0]);
//direct std out/error to pipefd[1], which is a fd write to parent process
dup2(pipefd[1], STDOUT_FILENO);
dup2(pipefd[1], STDERR_FILENO);
execv(cmd, argv);
//execv never return until finished
//if execv returns, there must be something wrong.
printf("execv error: %d\n", errno);
char * msg = strerror(errno);
printf("execv error:%s\n", msg);
}
else
{
//cpid > 0
//parent process
//when child process finished, init process will handle it.
signal(SIGCHLD, SIG_IGN);
//we do not write data to child process, so close write fd
close(pipefd[1]);
//read from child process
read(pipefd[0], buffer, 1023);
printf("get from child process:\n");
printf("%s\n", buffer);
//kill child process if necessary
kill(cpid, SIGTERM);
//wait child process finished
waitpid(cpid, NULL, 0);
printf("parent process finished.\n");
}
return 0;
}
int main()
{
int ret;
printf("enter main\n");
ret = entry_fun();
printf("exit main\n");
return ret;
}
编译运行结果为:
john@john-ThinkPad-Edge-E435:~/workspace/temp/c-test$ gcc pipe-test.c -o pipe-test
john@john-ThinkPad-Edge-E435:~/workspace/temp/c-test$ ls
pipe-test pipe-test.c pipe-test.c~
john@john-ThinkPad-Edge-E435:~/workspace/temp/c-test$ ./pipe-test
enter main
get from child process:
pipe-test
pipe-test.c
pipe-test.c~
parent process finished.
exit main
john@john-ThinkPad-Edge-E435:~/workspace/temp/c-test$
下面是一些函数调用的简单解释。
** pipe **
pipe调用生成两个管道,用于在亲子进程之间传递信息,需要两个元素的数组,这里是int pipefd[2],调用成功后,pipefd[0]表示读文件描述符,pipefd[1]表示写文件描述符。
通过man pipe得到的文档值得耐心读读。
PIPE(2) Linux Programmer's Manual PIPE(2)
NAME
pipe, pipe2 - create pipe
SYNOPSIS
#include <unistd.h>
int pipe(int pipefd[2]);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int pipe2(int pipefd[2], int flags);
DESCRIPTION
pipe() creates a pipe, a unidirectional data channel that can be used for interprocess communication. The array pipefd is used to
return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to
the write end of the pipe. Data written to the write end of the pipe is buffered by the kernel until it is read from the read end of
the pipe. For further details, see pipe(7).
If flags is 0, then pipe2() is the same as pipe(). The following values can be bitwise ORed in flags to obtain different behavior:
O_NONBLOCK Set the O_NONBLOCK file status flag on the two new open file descriptions. Using this flag saves extra calls to fcntl(2)
to achieve the same result.
O_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the two new file descriptors. See the description of the same flag in open(2)
for reasons why this may be useful.
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
ERRORS
EFAULT pipefd is not valid.
EINVAL (pipe2()) Invalid value in flags.
EMFILE Too many file descriptors are in use by the process.
ENFILE The system limit on the total number of open files has been reached.
VERSIONS
pipe2() was added to Linux in version 2.6.27; glibc support is available starting with version 2.9.
CONFORMING TO
pipe(): POSIX.1-2001.
pipe2() is Linux-specific.
EXAMPLE
The following program creates a pipe, and then fork(2)s to create a child process; the child inherits a duplicate set of file
descriptors that refer to the same pipe. After the fork(2), each process closes the descriptors that it doesn't need for the pipe
(see pipe(7)). The parent then writes the string contained in the program's command-line argument to the pipe, and the child reads
this string a byte at a time from the pipe and echoes it on standard output.
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
SEE ALSO
fork(2), read(2), socketpair(2), write(2), popen(3), pipe(7)
COLOPHON
This page is part of release 3.54 of the Linux man-pages project. A description of the project, and information about reporting
bugs, can be found at http://www.kernel.org/doc/man-pages/.
Linux 2012-02-14 PIPE(2)
fork
fork调用会创建子进程,返回值小于0表示出错,在子程序中检查返回值等于0,在父进程中检查返回值大于0.
还是看man文档吧,人家说的比较清楚,相信读者英语不成问题。
FORK(2) Linux Programmer's Manual FORK(2)
NAME
fork - create a child process
SYNOPSIS
#include <unistd.h>
pid_t fork(void);
DESCRIPTION
fork() creates a new process by duplicating the calling process. The new process, referred to as the child, is an exact duplicate of
the calling process, referred to as the parent, except for the following points:
* The child has its own unique process ID, and this PID does not match the ID of any existing process group (setpgid(2)).
* The child's parent process ID is the same as the parent's process ID.
* The child does not inherit its parent's memory locks (mlock(2), mlockall(2)).
* Process resource utilizations (getrusage(2)) and CPU time counters (times(2)) are reset to zero in the child.
* The child's set of pending signals is initially empty (sigpending(2)).
* The child does not inherit semaphore adjustments from its parent (semop(2)).
* The child does not inherit record locks from its parent (fcntl(2)).
* The child does not inherit timers from its parent (setitimer(2), alarm(2), timer_create(2)).
* The child does not inherit outstanding asynchronous I/O operations from its parent (aio_read(3), aio_write(3)), nor does it
inherit any asynchronous I/O contexts from its parent (see io_setup(2)).
The process attributes in the preceding list are all specified in POSIX.1-2001. The parent and child also differ with respect to the
following Linux-specific process attributes:
* The child does not inherit directory change notifications (dnotify) from its parent (see the description of F_NOTIFY in fcntl(2)).
* The prctl(2) PR_SET_PDEATHSIG setting is reset so that the child does not receive a signal when its parent terminates.
* The default timer slack value is set to the parent's current timer slack value. See the description of PR_SET_TIMERSLACK in
prctl(2).
* Memory mappings that have been marked with the madvise(2) MADV_DONTFORK flag are not inherited across a fork().
* The termination signal of the child is always SIGCHLD (see clone(2)).
* The port access permission bits set by ioperm(2) are not inherited by the child; the child must turn on any bits that it requires
using ioperm(2).
Note the following further points:
* The child process is created with a single thread—the one that called fork(). The entire virtual address space of the parent is
replicated in the child, including the states of mutexes, condition variables, and other pthreads objects; the use of
pthread_atfork(3) may be helpful for dealing with problems that this can cause.
* The child inherits copies of the parent's set of open file descriptors. Each file descriptor in the child refers to the same open
file description (see open(2)) as the corresponding file descriptor in the parent. This means that the two descriptors share open
file status flags, current file offset, and signal-driven I/O attributes (see the description of F_SETOWN and F_SETSIG in
fcntl(2)).
* The child inherits copies of the parent's set of open message queue descriptors (see mq_overview(7)). Each descriptor in the
child refers to the same open message queue description as the corresponding descriptor in the parent. This means that the two
descriptors share the same flags (mq_flags).
* The child inherits copies of the parent's set of open directory streams (see opendir(3)). POSIX.1-2001 says that the correspond‐
ing directory streams in the parent and child may share the directory stream positioning; on Linux/glibc they do not.
RETURN VALUE
On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in
the parent, no child process is created, and errno is set appropriately.
ERRORS
EAGAIN fork() cannot allocate sufficient memory to copy the parent's page tables and allocate a task structure for the child.
EAGAIN It was not possible to create a new process because the caller's RLIMIT_NPROC resource limit was encountered. To exceed this
limit, the process must have either the CAP_SYS_ADMIN or the CAP_SYS_RESOURCE capability.
ENOMEM fork() failed to allocate the necessary kernel structures because memory is tight.
ENOSYS fork() is not supported on this platform (for example, hardware without a Memory-Management Unit).
CONFORMING TO
SVr4, 4.3BSD, POSIX.1-2001.
NOTES
Under Linux, fork() is implemented using copy-on-write pages, so the only penalty that it incurs is the time and memory required to
duplicate the parent's page tables, and to create a unique task structure for the child.
Since version 2.3.3, rather than invoking the kernel's fork() system call, the glibc fork() wrapper that is provided as part of the
NPTL threading implementation invokes clone(2) with flags that provide the same effect as the traditional system call. (A call to
fork() is equivalent to a call to clone(2) specifying flags as just SIGCHLD.) The glibc wrapper invokes any fork handlers that have
been established using pthread_atfork(3).
EXAMPLE
See pipe(2) and wait(2).
SEE ALSO
clone(2), execve(2), exit(2), setrlimit(2), unshare(2), vfork(2), wait(2), daemon(3), capabilities(7), credentials(7)
COLOPHON
This page is part of release 3.54 of the Linux man-pages project. A description of the project, and information about reporting
bugs, can be found at http://www.kernel.org/doc/man-pages/.
Linux 2013-03-12 FORK(2)
dup2
dup2创建一个旧的文件描述符的复制。
这段代码里面,是把std out和error重定向到管道,使得父进程可以读取。
DUP(2) Linux Programmer's Manual DUP(2)
NAME
dup, dup2, dup3 - duplicate a file descriptor
SYNOPSIS
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int dup3(int oldfd, int newfd, int flags);
DESCRIPTION
These system calls create a copy of the file descriptor oldfd.
dup() uses the lowest-numbered unused descriptor for the new descriptor.
dup2() makes newfd be the copy of oldfd, closing newfd first if necessary, but note the following:
* If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.
* If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.
After a successful return from one of these system calls, the old and new file descriptors may be used interchangeably. They refer
to the same open file description (see open(2)) and thus share file offset and file status flags; for example, if the file offset is
modified by using lseek(2) on one of the descriptors, the offset is also changed for the other.
The two descriptors do not share file descriptor flags (the close-on-exec flag). The close-on-exec flag (FD_CLOEXEC; see fcntl(2))
for the duplicate descriptor is off.
dup3() is the same as dup2(), except that:
* The caller can force the close-on-exec flag to be set for the new file descriptor by specifying O_CLOEXEC in flags. See the
description of the same flag in open(2) for reasons why this may be useful.
* If oldfd equals newfd, then dup3() fails with the error EINVAL.
RETURN VALUE
On success, these system calls return the new descriptor. On error, -1 is returned, and errno is set appropriately.
ERRORS
EBADF oldfd isn't an open file descriptor, or newfd is out of the allowed range for file descriptors.
EBUSY (Linux only) This may be returned by dup2() or dup3() during a race condition with open(2) and dup().
EINTR The dup2() or dup3() call was interrupted by a signal; see signal(7).
EINVAL (dup3()) flags contain an invalid value. Or, oldfd was equal to newfd.
EMFILE The process already has the maximum number of file descriptors open and tried to open a new one.
VERSIONS
dup3() was added to Linux in version 2.6.27; glibc support is available starting with version 2.9.
CONFORMING TO
dup(), dup2(): SVr4, 4.3BSD, POSIX.1-2001.
dup3() is Linux-specific.
NOTES
The error returned by dup2() is different from that returned by fcntl(..., F_DUPFD, ...) when newfd is out of range. On some sys‐
tems dup2() also sometimes returns EINVAL like F_DUPFD.
If newfd was open, any errors that would have been reported at close(2) time are lost. A careful programmer will not use dup2() or
dup3() without closing newfd first.
SEE ALSO
close(2), fcntl(2), open(2)
COLOPHON
This page is part of release 3.54 of the Linux man-pages project. A description of the project, and information about reporting
bugs, can be found at http://www.kernel.org/doc/man-pages/.
Linux 2012-02-14 DUP(2)
execv
execv装入并运行其他的命令。exe家族有很多个,重点了解一个,其他的就容易了。
EXEC(3) Linux Programmer's Manual EXEC(3)
NAME
execl, execlp, execle, execv, execvp, execvpe - execute a file
SYNOPSIS
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
execvpe(): _GNU_SOURCE
DESCRIPTION
The exec() family of functions replaces the current process image with a new process image. The functions described in this manual
page are front-ends for execve(2). (See the manual page for execve(2) for further details about the replacement of the current
process image.)
The initial argument for these functions is the name of a file that is to be executed.
The const char *arg and subsequent ellipses in the execl(), execlp(), and execle() functions can be thought of as arg0, arg1, ...,
argn. Together they describe a list of one or more pointers to null-terminated strings that represent the argument list available to
the executed program. The first argument, by convention, should point to the filename associated with the file being executed. The
list of arguments must be terminated by a NULL pointer, and, since these are variadic functions, this pointer must be cast (char *)
NULL.
The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument
list available to the new program. The first argument, by convention, should point to the filename associated with the file being
executed. The array of pointers must be terminated by a NULL pointer.
The execle() and execvpe() functions allow the caller to specify the environment of the executed program via the argument envp. The
envp argument is an array of pointers to null-terminated strings and must be terminated by a NULL pointer. The other functions take
the environment for the new process image from the external variable environ in the calling process.
Special semantics for execlp() and execvp()
The execlp(), execvp(), and execvpe() functions duplicate the actions of the shell in searching for an executable file if the speci‐
fied filename does not contain a slash (/) character. The file is sought in the colon-separated list of directory pathnames speci‐
fied in the PATH environment variable. If this variable isn't defined, the path list defaults to the current directory followed by
the list of directories returned by confstr(_CS_PATH). (This confstr(3) call typically returns the value "/bin:/usr/bin".)
If the specified filename includes a slash character, then PATH is ignored, and the file at the specified pathname is executed.
In addition, certain errors are treated specially.
If permission is denied for a file (the attempted execve(2) failed with the error EACCES), these functions will continue searching
the rest of the search path. If no other file is found, however, they will return with errno set to EACCES.
If the header of a file isn't recognized (the attempted execve(2) failed with the error ENOEXEC), these functions will execute the
shell (/bin/sh) with the path of the file as its first argument. (If this attempt fails, no further searching is done.)
RETURN VALUE
The exec() functions return only if an error has occurred. The return value is -1, and errno is set to indicate the error.
ERRORS
All of these functions may fail and set errno for any of the errors specified for execve(2).
VERSIONS
The execvpe() function first appeared in glibc 2.11.
CONFORMING TO
POSIX.1-2001, POSIX.1-2008.
The execvpe() function is a GNU extension.
NOTES
On some other systems, the default path (used when the environment does not contain the variable PATH) has the current working direc‐
tory listed after /bin and /usr/bin, as an anti-Trojan-horse measure. Linux uses here the traditional "current directory first"
default path.
The behavior of execlp() and execvp() when errors occur while attempting to execute the file is historic practice, but has not tradi‐
tionally been documented and is not specified by the POSIX standard. BSD (and possibly other systems) do an automatic sleep and
retry if ETXTBSY is encountered. Linux treats it as a hard error and returns immediately.
Traditionally, the functions execlp() and execvp() ignored all errors except for the ones described above and ENOMEM and E2BIG, upon
which they returned. They now return if any error other than the ones described above occurs.
SEE ALSO
sh(1), execve(2), fork(2), ptrace(2), fexecve(3), environ(7)
COLOPHON
This page is part of release 3.54 of the Linux man-pages project. A description of the project, and information about reporting
bugs, can be found at http://www.kernel.org/doc/man-pages/.
GNU 2010-09-25 EXEC(3)
signal
子进程在结束的时候会向父进程发送信号SIGCHLD,父进程可以设置SIGCHLD的处理微SIG_IGN,表示子进程的处理由init来完成。
如果父进程很忙,可以注册信号处理函数来处理。
信号内容很多,先看signal的文档吧。
SIGNAL(2) Linux Programmer's Manual SIGNAL(2)
NAME
signal - ANSI C signal handling
SYNOPSIS
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
DESCRIPTION
The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its
use: use sigaction(2) instead. See Portability below.
signal() sets the disposition of the signal signum to handler, which is either SIG_IGN, SIG_DFL, or the address of a programmer-
defined function (a "signal handler").
If the signal signum is delivered to the process, then one of the following happens:
* If the disposition is set to SIG_IGN, then the signal is ignored.
* If the disposition is set to SIG_DFL, then the default action associated with the signal (see signal(7)) occurs.
* If the disposition is set to a function, then first either the disposition is reset to SIG_DFL, or the signal is blocked (see
Portability below), and then handler is called with argument signum. If invocation of the handler caused the signal to be
blocked, then the signal is unblocked upon return from the handler.
The signals SIGKILL and SIGSTOP cannot be caught or ignored.
RETURN VALUE
signal() returns the previous value of the signal handler, or SIG_ERR on error. In the event of an error, errno is set to indicate
the cause.
ERRORS
EINVAL signum is invalid.
CONFORMING TO
C89, C99, POSIX.1-2001.
NOTES
The effects of signal() in a multithreaded process are unspecified.
According to POSIX, the behavior of a process is undefined after it ignores a SIGFPE, SIGILL, or SIGSEGV signal that was not gener‐
ated by kill(2) or raise(3). Integer division by zero has undefined result. On some architectures it will generate a SIGFPE signal.
(Also dividing the most negative integer by -1 may generate SIGFPE.) Ignoring this signal might lead to an endless loop.
See sigaction(2) for details on what happens when SIGCHLD is set to SIG_IGN.
See signal(7) for a list of the async-signal-safe functions that can be safely called from inside a signal handler.
The use of sighandler_t is a GNU extension, exposed if _GNU_SOURCE is defined; glibc also defines (the BSD-derived) sig_t if
_BSD_SOURCE is defined. Without use of such a type, the declaration of signal() is the somewhat harder to read:
void ( *signal(int signum, void (*handler)(int)) ) (int);
Portability
The only portable use of signal() is to set a signal's disposition to SIG_DFL or SIG_IGN. The semantics when using signal() to
establish a signal handler vary across systems (and POSIX.1 explicitly permits this variation); do not use it for this purpose.
POSIX.1 solved the portability mess by specifying sigaction(2), which provides explicit control of the semantics when a signal han‐
dler is invoked; use that interface instead of signal().
In the original UNIX systems, when a handler that was established using signal() was invoked by the delivery of a signal, the dispo‐
sition of the signal would be reset to SIG_DFL, and the system did not block delivery of further instances of the signal. This is
equivalent to calling sigaction(2) with the following flags:
sa.sa_flags = SA_RESETHAND | SA_NODEFER;
System V also provides these semantics for signal(). This was bad because the signal might be delivered again before the handler had
a chance to reestablish itself. Furthermore, rapid deliveries of the same signal could result in recursive invocations of the han‐
dler.
BSD improved on this situation, but unfortunately also changed the semantics of the existing signal() interface while doing so. On
BSD, when a signal handler is invoked, the signal disposition is not reset, and further instances of the signal are blocked from
being delivered while the handler is executing. Furthermore, certain blocking system calls are automatically restarted if inter‐
rupted by a signal handler (see signal(7)). The BSD semantics are equivalent to calling sigaction(2) with the following flags:
sa.sa_flags = SA_RESTART;
The situation on Linux is as follows:
* The kernel's signal() system call provides System V semantics.
* By default, in glibc 2 and later, the signal() wrapper function does not invoke the kernel system call. Instead, it calls sigac‐
tion(2) using flags that supply BSD semantics. This default behavior is provided as long as the _BSD_SOURCE feature test macro is
defined. By default, _BSD_SOURCE is defined; it is also implicitly defined if one defines _GNU_SOURCE, and can of course be
explicitly defined.
On glibc 2 and later, if the _BSD_SOURCE feature test macro is not defined, then signal() provides System V semantics. (The
default implicit definition of _BSD_SOURCE is not provided if one invokes gcc(1) in one of its standard modes (-std=xxx or -ansi)
or defines various other feature test macros such as _POSIX_SOURCE, _XOPEN_SOURCE, or _SVID_SOURCE; see feature_test_macros(7).)
* The signal() function in Linux libc4 and libc5 provide System V semantics. If one on a libc5 system includes <bsd/signal.h>
instead of <signal.h>, then signal() provides BSD semantics.
SEE ALSO
kill(1), alarm(2), kill(2), killpg(2), pause(2), sigaction(2), signalfd(2), sigpending(2), sigprocmask(2), sigsuspend(2), bsd_sig‐
nal(3), raise(3), siginterrupt(3), sigqueue(3), sigsetops(3), sigvec(3), sysv_signal(3), signal(7)
COLOPHON
This page is part of release 3.54 of the Linux man-pages project. A description of the project, and information about reporting
bugs, can be found at http://www.kernel.org/doc/man-pages/.
Linux 2013-04-19 SIGNAL(2)
waitpid
waitpid会暂时停止目前进程的执行,直到有信号来到或子进程结束。仔细读读文档。
WAIT(2) Linux Programmer's Manual WAIT(2)
NAME
wait, waitpid, waitid - wait for process to change state
SYNOPSIS
#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);
/* This is the glibc and POSIX interface; see
NOTES for information on the raw system call. */
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
waitid():
_SVID_SOURCE || _XOPEN_SOURCE >= 500 || _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED
|| /* Since glibc 2.12: */ _POSIX_C_SOURCE >= 200809L
DESCRIPTION
All of these system calls are used to wait for state changes in a child of the calling process, and obtain information about the
child whose state has changed. A state change is considered to be: the child terminated; the child was stopped by a signal; or the
child was resumed by a signal. In the case of a terminated child, performing a wait allows the system to release the resources asso‐
ciated with the child; if a wait is not performed, then the terminated child remains in a "zombie" state (see NOTES below).
If a child has already changed state, then these calls return immediately. Otherwise they block until either a child changes state
or a signal handler interrupts the call (assuming that system calls are not automatically restarted using the SA_RESTART flag of
sigaction(2)). In the remainder of this page, a child whose state has changed and which has not yet been waited upon by one of these
system calls is termed waitable.
wait() and waitpid()
The wait() system call suspends execution of the calling process until one of its children terminates. The call wait(&status) is
equivalent to:
waitpid(-1, &status, 0);
The waitpid() system call suspends execution of the calling process until a child specified by pid argument has changed state. By
default, waitpid() waits only for terminated children, but this behavior is modifiable via the options argument, as described below.
The value of pid can be:
< -1 meaning wait for any child process whose process group ID is equal to the absolute value of pid.
-1 meaning wait for any child process.
0 meaning wait for any child process whose process group ID is equal to that of the calling process.
> 0 meaning wait for the child whose process ID is equal to the value of pid.
The value of options is an OR of zero or more of the following constants:
WNOHANG return immediately if no child has exited.
WUNTRACED also return if a child has stopped (but not traced via ptrace(2)). Status for traced children which have stopped is pro‐
vided even if this option is not specified.
WCONTINUED (since Linux 2.6.10)
also return if a stopped child has been resumed by delivery of SIGCONT.
(For Linux-only options, see below.)
If status is not NULL, wait() and waitpid() store status information in the int to which it points. This integer can be inspected
with the following macros (which take the integer itself as an argument, not a pointer to it, as is done in wait() and waitpid()!):
WIFEXITED(status)
returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main().
WEXITSTATUS(status)
returns the exit status of the child. This consists of the least significant 8 bits of the status argument that the child
specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main(). This macro should be employed
only if WIFEXITED returned true.
WIFSIGNALED(status)
returns true if the child process was terminated by a signal.
WTERMSIG(status)
returns the number of the signal that caused the child process to terminate. This macro should be employed only if WIFSIG‐
NALED returned true.
WCOREDUMP(status)
returns true if the child produced a core dump. This macro should be employed only if WIFSIGNALED returned true. This macro
is not specified in POSIX.1-2001 and is not available on some UNIX implementations (e.g., AIX, SunOS). Only use this enclosed
in #ifdef WCOREDUMP ... #endif.
WIFSTOPPED(status)
returns true if the child process was stopped by delivery of a signal; this is possible only if the call was done using WUN‐
TRACED or when the child is being traced (see ptrace(2)).
WSTOPSIG(status)
returns the number of the signal which caused the child to stop. This macro should be employed only if WIFSTOPPED returned
true.
WIFCONTINUED(status)
(since Linux 2.6.10) returns true if the child process was resumed by delivery of SIGCONT.
waitid()
The waitid() system call (available since Linux 2.6.9) provides more precise control over which child state changes to wait for.
The idtype and id arguments select the child(ren) to wait for, as follows:
idtype == P_PID
Wait for the child whose process ID matches id.
idtype == P_PGID
Wait for any child whose process group ID matches id.
idtype == P_ALL
Wait for any child; id is ignored.
The child state changes to wait for are specified by ORing one or more of the following flags in options:
WEXITED Wait for children that have terminated.
WSTOPPED Wait for children that have been stopped by delivery of a signal.
WCONTINUED Wait for (previously stopped) children that have been resumed by delivery of SIGCONT.
The following flags may additionally be ORed in options:
WNOHANG As for waitpid().
WNOWAIT Leave the child in a waitable state; a later wait call can be used to again retrieve the child status information.
Upon successful return, waitid() fills in the following fields of the siginfo_t structure pointed to by infop:
si_pid The process ID of the child.
si_uid The real user ID of the child. (This field is not set on most other implementations.)
si_signo Always set to SIGCHLD.
si_status Either the exit status of the child, as given to _exit(2) (or exit(3)), or the signal that caused the child to terminate,
stop, or continue. The si_code field can be used to determine how to interpret this field.
si_code Set to one of: CLD_EXITED (child called _exit(2)); CLD_KILLED (child killed by signal); CLD_DUMPED (child killed by sig‐
nal, and dumped core); CLD_STOPPED (child stopped by signal); CLD_TRAPPED (traced child has trapped); or CLD_CONTINUED
(child continued by SIGCONT).
If WNOHANG was specified in options and there were no children in a waitable state, then waitid() returns 0 immediately and the state
of the siginfo_t structure pointed to by infop is unspecified. To distinguish this case from that where a child was in a waitable
state, zero out the si_pid field before the call and check for a nonzero value in this field after the call returns.
RETURN VALUE
wait(): on success, returns the process ID of the terminated child; on error, -1 is returned.
waitpid(): on success, returns the process ID of the child whose state has changed; if WNOHANG was specified and one or more
child(ren) specified by pid exist, but have not yet changed state, then 0 is returned. On error, -1 is returned.
waitid(): returns 0 on success or if WNOHANG was specified and no child(ren) specified by id has yet changed state; on error, -1 is
returned. Each of these calls sets errno to an appropriate value in the case of an error.
ERRORS
ECHILD (for wait()) The calling process does not have any unwaited-for children.
ECHILD (for waitpid() or waitid()) The process specified by pid (waitpid()) or idtype and id (waitid()) does not exist or is not a
child of the calling process. (This can happen for one's own child if the action for SIGCHLD is set to SIG_IGN. See also the
Linux Notes section about threads.)
EINTR WNOHANG was not set and an unblocked signal or a SIGCHLD was caught; see signal(7).
EINVAL The options argument was invalid.
CONFORMING TO
SVr4, 4.3BSD, POSIX.1-2001.
NOTES
A child that terminates, but has not been waited for becomes a "zombie". The kernel maintains a minimal set of information about the
zombie process (PID, termination status, resource usage information) in order to allow the parent to later perform a wait to obtain
information about the child. As long as a zombie is not removed from the system via a wait, it will consume a slot in the kernel
process table, and if this table fills, it will not be possible to create further processes. If a parent process terminates, then
its "zombie" children (if any) are adopted by init(8), which automatically performs a wait to remove the zombies.
POSIX.1-2001 specifies that if the disposition of SIGCHLD is set to SIG_IGN or the SA_NOCLDWAIT flag is set for SIGCHLD (see sigac‐
tion(2)), then children that terminate do not become zombies and a call to wait() or waitpid() will block until all children have
terminated, and then fail with errno set to ECHILD. (The original POSIX standard left the behavior of setting SIGCHLD to SIG_IGN
unspecified. Note that even though the default disposition of SIGCHLD is "ignore", explicitly setting the disposition to SIG_IGN
results in different treatment of zombie process children.) Linux 2.6 conforms to this specification. However, Linux 2.4 (and ear‐
lier) does not: if a wait() or waitpid() call is made while SIGCHLD is being ignored, the call behaves just as though SIGCHLD were
not being ignored, that is, the call blocks until the next child terminates and then returns the process ID and status of that child.
Linux notes
In the Linux kernel, a kernel-scheduled thread is not a distinct construct from a process. Instead, a thread is simply a process
that is created using the Linux-unique clone(2) system call; other routines such as the portable pthread_create(3) call are imple‐
mented using clone(2). Before Linux 2.4, a thread was just a special case of a process, and as a consequence one thread could not
wait on the children of another thread, even when the latter belongs to the same thread group. However, POSIX prescribes such func‐
tionality, and since Linux 2.4 a thread can, and by default will, wait on children of other threads in the same thread group.
The following Linux-specific options are for use with children created using clone(2); they cannot be used with waitid():
__WCLONE
Wait for "clone" children only. If omitted then wait for "non-clone" children only. (A "clone" child is one which delivers
no signal, or a signal other than SIGCHLD to its parent upon termination.) This option is ignored if __WALL is also speci‐
fied.
__WALL (since Linux 2.4)
Wait for all children, regardless of type ("clone" or "non-clone").
__WNOTHREAD (since Linux 2.4)
Do not wait for children of other threads in the same thread group. This was the default before Linux 2.4.
The raw waitid() system call takes a fith argument, of type struct rusage *. If this argument is non-NULL, then it is used to return
resource usage information about the child, in the same manner as wait4(2). See getrusage(2) for details.
BUGS
According to POSIX.1-2008, an application calling waitid() must ensure that infop points to a siginfo_t structure (i.e., that it is a
non-NULL pointer). On Linux, if infop is NULL, waitid() succeeds, and returns the process ID of the waited-for child. Applications
should avoid relying on this inconsistent, nonstandard, and unnecessary feature.
EXAMPLE
The following program demonstrates the use of fork(2) and waitpid(). The program creates a child process. If no command-line argu‐
ment is supplied to the program, then the child suspends its execution using pause(2), to allow the user to send signals to the
child. Otherwise, if a command-line argument is supplied, then the child exits immediately, using the integer supplied on the com‐
mand line as the exit status. The parent process executes a loop that monitors the child using waitpid(), and uses the W*() macros
described above to analyze the wait status value.
The following shell session demonstrates the use of the program:
$ ./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 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);
}
}
SEE ALSO
_exit(2), clone(2), fork(2), kill(2), ptrace(2), sigaction(2), signal(2), wait4(2), pthread_create(3), credentials(7), signal(7)
COLOPHON
This page is part of release 3.54 of the Linux man-pages project. A description of the project, and information about reporting
bugs, can be found at http://www.kernel.org/doc/man-pages/.
Linux 2013-09-04 WAIT(2)
网友评论