[IPC communication] Signal processing interface Signal API (6)

        The idea of ​​sending and receiving signals is one of the features of Linux programming. A signal can be considered as a soft interrupt, which is used to notify the process of asynchronous events.

        The signal processing content described in this article comes from Linux man . This article mainly introduces each API in detail to better understand signal programming.


wait(2)

Comply with POSIX.1-2008

1.Library

标准 c 库,libc, -lc

2.Interface definition

        This interface relies on the _POSIX_C_SOURCE attribute test macro.

  #include <sys/wait.h>

       pid_t wait(int *_Nullable wstatus);
       pid_t waitpid(pid_t pid, int *_Nullable wstatus, 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. */
        waitid():
           Since glibc 2.26:
               _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
           glibc 2.25 and earlier:
               _XOPEN_SOURCE
                   || /* Since glibc 2.12: */ _POSIX_C_SOURCE >= 200809L
                   || /* glibc <= 2.19: */ _BSD_SOURCE

3.Interface description

        All of these system calls are used by the calling process to wait for the state changes of the child process and obtain information related to the changed process. The state changes here include: the child process is terminated, the child process is interrupted by a signal, and the child process is resumed by a signal. For the terminated child process, the process wait operation allows the system to release the related resources of the child process. If wait is not performed, the terminated child process will stay in the zombie state (refer to the following note).

        If the child process has already changed state, these calls will return immediately. Otherwise, it will block until the state of the child process changes or a signal processing function interrupts the current call (we assume that the signal does not set the SA_RESTART flag of sigaction(2)). In this article, the state of the child process that changes but is not waited is called waitable.

        wait() 和 waitpid()

        The wait() system call suspends the current calling thread until any child process terminates. The equivalent of wait(&wstatus)

        waitpid(-1, &wstatus, 0)

        The waitpid() system call suspends the current calling thread until the state of the child process specified by pid changes. By default, waitpid() only waits for terminated child processes, but this behavior will be affected by the options parameter, which will be discussed later.

        The value of pid can be:

        < -1 means waiting for all child processes whose process group ID is the absolute value of pid

        -1 means wait for any child process

        0 means waiting for any child process whose process group ID at the time of calling is the same as the process group ID of the calling process.

        > 0 means waiting for the child process with process ID pid

        The value of options is the bitwise OR of the following values:

        WNOHANG

                If no child process exits, then return immediately

        WUNTRACED

                Also returned when the child process stops (but is not traced by ptrace(2)). The stopped status of the tracked child process is also returned when this status is not specified.

        Here are some options for Linux only:

        If wstatus is not NULL, wait() and waitpid() will store the status information into the integer pointed to by it. This number can be checked by the following macro.

        WIFEXITED(wstatus)

        Returns true if the child process terminates normally, that is, through the exit(3), _exit(2) or main() function.

        WEXISTSTATUS(wstatus)

        Returns the exit status of the child process. The status includes the lower 8 bits of the parameter status when the child process calls exit(3), _exit(2) or main() and returns. It is applicable only when WIFEXISTED returns true.

        WIFSIGNALED(wstatus)

        Returns true if the child process was terminated by a signal.

        WTERMSIG(wstatus)

        Returns the signal number that caused the child process to exit, only applicable when WIFSIGNALED returns true.

        WCOREDUMP(wstatus)

        Returns true if the child process generates a core dump (see core(5)). This macro only applies when WIFSIGNALED returns true.

        This macro is not described in POSIX.1-2001 and is not available on some UNIX implementations (eg AIX, SunOS). Therefore, #ifdef WCOREDUMP should be added when using it... #endif

        WIFSTOPPED(wstatus)

        Returns true when the child process is stopped by a signal. This is only applicable when WUNTRACED is called or the child process is being traced.

        WSTOPSIG (wstatus)

        Returns the signal that caused the child process to stop. This macro can only be applied when WIFSTOPPED returns true.

        WIFCONTINUED(wstatus)

        Starting from Linux 2.6.10, true is returned when the child process receives the SIGCONT signal and continues execution.

waitpid()

        The waitpid() system call (since Linux 2.6.9) provides more precise control over child process state changes.

        The idtype and id parameters are used to select the child process to wait for, as follows:

        idtype == P_PID

                Waiting for the child process with process ID number id

        idtype == P_PIDFD (after Linux 5.4)

                Waits for the child process specified by file descriptor id.

        idtype == P_PGID

                Waiting for the child process with process group ID id. After Linux 5.4, if the id is 0, wait for the calling time and all child processes in the same process group of the calling process.

        idtype == P_ALL

                Wait for any child processes, ignoring id.

        The state to wait for is specified by options, which is the bitwise OR of the following markers:

        WEXITED

                Wait for terminated child process

        WSTOPPED

                Waiting for a child process to be stopped by a signal

        WCONTINUED

                Child process waiting to be resumed by SIGCONT signal (previously stopped)

        The following tags may also be used in options:

        WNOHANG

                Same as waitpid()

        WNOWAIT

                Let the child process continue to be in the waiting state, and subsequent calls can continue to obtain the child process status information.

        Once successfully returned, waitpid() will populate the siginfo_t structure pointed to by infop:

        si_pid child process process id

        si_uid The real user ID of the child process (most implementations do not set this field)

        si_signo is always SIGCHLD

        The return status of si_status _exit(2), exit(3), or the signal that causes the child process to terminate, stop, or continue. The si_code field can be used to determine how to translate this field.

        si_code can be set to: CLD_EXITED (the child process called _exit(2)), CLD_KILLED (the child process was killed by a signal), CLD_DUMPED (the child process was killed by a signal and core dumped), CLD_TRAPPED (trace the child process stopped), and CLD_CONTINUED (the child process is continued by the SIGCONT signal).

        If WNOHANG is specified in options and no child process is in the waitable state, waitpid() returns 0 immediately and the siginfo_t structure pointed to by infop is filled in an implementation-defined manner. To distinguish from the case where there is a waitable child process, the si_pid field needs to be cleared before calling and checking whether this field is non-zero.

        POSIX.1-2008 Technical Corrigendum 1 (2013) added some requirements that waitpid() should clear the si_pid and si_signo fields when WNOHANG is specified and no child processes are waitable. On Linux and its system implementations that comply with this requirement, si_pid does not need to be cleared when calling waitpid(). However, at this point, not all implementations follow the POSIX.1 specification.        

4.Return value

        wait(): Returns the ID of the terminated child process on success, and -1 on failure.

        waitpid(): Returns the ID of the child process whose status changes when successful. If WNOHANG is specified and one or more child processes are specified through pid, but no status change occurs, 0 will be returned. Returns -1 on failure.

        waitid(): Returns 0 when successful or WNOHANG is specified and the child process specified by id has no status change, and -1 when failed.

        On failure, errno is updated.

        The error code is as follows:

EAGAIN PID file descriptor id is non-blocking and the process it points to has not terminated
ECHILD The calling process does not have any unwaited child processes (wait())
ECHILD (waitpid() or waitid()) The process specified by pid or the process specified by idtype and id does not exist, or is not a child process of the calling process. (This may occur when the child process sets the SIGCHILD signal to SIG_IGN)
EINTR WNOHANG was not specified and a non-blocking signal or SIGCHILD was caught
SINGLE CHOICE options parameter is illegal
ESRCH pid is equal to INT_MIN (wait(), waitpid())

5.History

        wait() is actually a library function implemented by libc, which calls wait4(2).

        On some architectures, there is no waitpid() system call. This interface is also implemented by the C library through the wait4(2) system call.

        The waitid() system call has a fifth parameter, the type is struct rusage *. If this parameter is not empty, it will be used to return the resource usage information of the child process. This is similar to wait4(2). See getrusage(2) for more details.

 6.Attention

       A child process that terminates but is not waited for is called a zombie process. The kernel maintains a minimum set of information about the zombie process (PID, termination status, resource usage), which is used by the parent process to wait later to obtain relevant information about the child process. As long as the zombie process is not removed from the system through wait, it will occupy a position in the kernel process table. If the table is full, no more processes can be created. If the parent process terminates, its zombie child processes will be taken over by init process No. 1 (or adopted by the nearest subreaper specified by prctl(2) PR_SET_CHILD_SUBREAPER), and process No. 1 will automatically process wait to remove these zombies.

        POSIX.1-2001 specifies that if the disposal function of SIGCHLD is set to SIG_IGN or the SA_NOCLDWAIT flag of SIGCHLD is set, then the child process will not become a zombie process after it terminates, and the wait() or waitpid() call will block until all child processes terminate. , and then setting errrno to ECHILD failed. (The original POSIX standard did not define behavior in this case; note that although the default handler for SIGCHLD is SIG_IGN, an explicit SIG_IGN setting will behave differently for zombie child processes.)

        Linux 2.6 follows POSIX requirements. However, Linux 2.4 and earlier versions do not: if wait() or waitpid() is called when SIGCHLD is ignored, the calling behavior is the same as if SIGCHLD is not set, which means that the call will block until the next subroutine. The process terminates and then returns the corresponding process ID and status information.

7.BUGS

      According to POSIX.1-2008, when the application calls waitid(), it must ensure that infop points to the siinfo_t structure (that is, it cannot be a null pointer). On Linux, if infop is NULL, waitid() succeeds and returns the waiting process ID. Applications should avoid relying on this inconsistent, non-standard, and unnecessary feature.

8.Code

         The following program demonstrates the use of fork(2) and waitpid(). The program creates a child process. If no command line arguments are provided, the child process is stopped using pause(2), allowing the user to send signals to it. Otherwise, if a command line argument is specified, the child process returns immediately, using the integer provided on the command line as the return status value. The parent process executes in a loop, monitors the child process through waitpid(), and uses the W*() macro to analyze the returned 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
           $

        The following is the program source code: 

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

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

           cpid = fork();
           if (cpid == -1) {
               perror("fork");
               exit(EXIT_FAILURE);
           }

           if (cpid == 0) {            /* Code executed by child */
               printf("Child PID is %jd\n", (intmax_t) getpid());
               if (argc == 1)
                   pause();                    /* Wait for signals */
               _exit(atoi(argv[1]));

           } else {                    /* Code executed by parent */
               do {
                   w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
                   if (w == -1) {
                       perror("waitpid");
                       exit(EXIT_FAILURE);
                   }

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

Guess you like

Origin blog.csdn.net/BillyThe/article/details/133394478