美文网首页
简单的进程间通信模型

简单的进程间通信模型

作者: tjy_2011 | 来源:发表于2018-08-20 22:28 被阅读0次

    简单的进程间通信模型

    关键字: 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)
    

    相关文章

      网友评论

          本文标题:简单的进程间通信模型

          本文链接:https://www.haomeiwen.com/subject/yufriftx.html