简单的进程间通信模型

简单的进程间通信模型

关键字: fork, pipe, dup2, signal, SIGCHLD

20180820 tjy

转载请注明出处

先上代码,后面有解释。

#include 
#include 
#include 
#include 
#include 

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 

       int pipe(int pipefd[2]);

       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include               /* Obtain O_* constant definitions */
       #include 

       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 
       #include 
       #include 
       #include 
       #include 

       int
       main(int argc, char *argv[])
       {
           int pipefd[2];
           pid_t cpid;
           char buf;

           if (argc != 2) {
            fprintf(stderr, "Usage: %s \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 

       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 

       int dup(int oldfd);
       int dup2(int oldfd, int newfd);

       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include               /* Obtain O_* constant definitions */
       #include 

       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 

       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 

       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 
         instead of , 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 
       #include 

       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 
       #include 
       #include 
       #include 

       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)

你可能感兴趣的:(简单的进程间通信模型)