[minidive bash] waitpid

pinelancer·2021년 10월 24일
0

minishell

목록 보기
4/6

How to use 'waitpid()'

Sigaction()

described in [1, Vol. 2, ch. 3, sigaction()]

NAME

sigaction — examine and change a signal action

SYNOPSIS

#include <signal.h>
int sigaction(int sig, const struct sigaction *restrict act, 
	struct sigaction *restrict oact);

DESCRIPTION

The sigaction() function allows the calling process to examine and/or specify the action to b associated with a specific signal. The argument sig specifies the signal; acceptable values ar defined in \<signal.h>.

The structure sigaction, used to describe an action to be taken, is defined in the \<signal.h> header to include at least the following members:

Member TypeMemberDescription
void(*) (int)
sigset_t

int void(*) (int,
siginfo_t *, void *)
sa_handler sa_mask
sa_flags
sa_sigaction
Pointer to a signal-catching function or one of the macros SIG_IGN or SIG_DFL.
Additional set of signals to be blocked during execution of signal-catching function.
Special flags to affect behavior of signal. Pointer to a signal-catching function.

The storage occupied by sa_handler and sa_sigaction may overlap, and a conforming application shall not use both simultaneously.

...

If the SA_SIGINFO flag (see below) is cleared in the sa_flags field of the sigaction structure, th sa_handler field identifies the action to be associated with the specified signal. If th SA_SIGINFO flag is set in the sa_flags field, the sa_sigaction field specifies a signal-catchin function.

  • sa_flags:

    • SA_SIGINFO

      If cleared and the signal is caught, the signal-catching function shall be entered as:
      
      void func(int signo);
      
      where *signo* is the only argument to the signal-catching function. In this case, the application shall use the *sa_handler* member to describe the signal-catching function and the application shall not modify the *sa_sigaction* member.
      
      If SA_SIGINFO is set and the signal is caught, the signal-catching function shall be entered as:
      
      void func(int signo, siginfo_t *info, void *context);
      
      where two additional arguments are passed to the signal-catching function. The second argument shall point to an object of type **siginfo_t** explaining the reason why the signal was generated; the third argument can be cast to a pointer to an object of type **ucontext_t** to refer to the receiving thread’s context that was interrupted when the signal was delivered. In this case, the application shall use the *sa_sigaction* member to describe the signal-catching function and the application shall not modify the *sa_handler* member.
    • SA_NOCLDWAIT

      If set, and *sig* equals SIGCHLD, child processes of the calling processes shall not be transformed into zombie processes when they terminate. If the calling process subsequently waits for its children, and the process has no unwaited-for children that were transformed into zombie processes, it shall block until all of its children terminate, and *wait*(), *waitid*(), and *waitpid*() shall fail and set *errno* to [ECHILD]. Otherwise, terminating child processes shall be transformed into zombie processes, unless SIGCHLD is set to SIG_IGN.
- ...

wait()

described in [1, Vol. 2, ch. 3, wait()]

NAME

wait, waitpid — wait for a child process to stop or terminate

SYNOPSIS

#include <sys/wait.h> pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid, int *stat_loc, int options);

DESCRIPTION

...

Otherwise, if SIGCHLD is blocked, if wait() or waitpid() return because the status of a chil process is available, any pending SIGCHLD signal shall be cleared unless the status of anothe child process is available.

For all other conditions, it is unspecified whether child status will be available when a SIGCHL signal is delivered.

ERRORS

The waitpid ( ) function shall fail if:

  • [ECHILD]

    The process specified by pid does not exist or is not a child of the callin process, or the process group specified by pid does not exist or does not hav any member process that is a child of the calling process.

  • [EINTR]

    The function was interrupted by a signal. The value of the location pointed to by stat_loc is undefined.

  • [EINVAL]

    The options argument is not valid.

APPLICATION USAGE

Calls to wait() will collect information about any child process. This may result in interactions with other interfaces that may be waiting for their own children (such as by use of system ( )). For this and other reasons it is recommended that portable applications not use wait(), but instead use waitpid ( ). For these same reasons, the use of waitpid ( ) with a pid argument of −1, and the use of waitid() with the idtype argument set to P_ALL, are also not recommended for portable applications.

RATIONALE

A call to the wait ( ) or waitpid ( ) function only returns status on an immediate child process of the calling process; that is, a child that was produced by a single fork ( ) call (perhaps followed by an exec or other function calls) from the parent. If a child produces grandchildren by further use of fork(), none of those grandchildren nor any of their descendants affect the behavior of a wait() from the original parent process. Nothing in this volume of POSIX.1-2008 prevents an implementation from providing extensions that permit a process to get status from a grandchild or any other process, but a process that does not use such extensions must be guaranteed to see status from only its direct children.

The waitpid ( ) function is provided for three reasons:

  1. To support job control
  2. To permit a non-blocking version of the wait ( ) function
  3. To permit a library routine, such as system() or pclose(), to wait for its children without interfering with other terminated children for which the process has not waited

The first two of these facilities are based on the wait3() function provided by 4.3 BSD. The function uses the options argument, which is equivalent to an argument to wait3().

The third reason for the waitpid() function is to permit independent sections of a process to spawn and wait for children without interfering with each other.

...

Allowing the wait() family of functions to discard a pending SIGCHLD signal that is associated with a successfully waited-for child process puts them into the sigwait() and sigwaitinfo() category with respect to SIGCHLD.

This definition allows implementations to treat a pending SIGCHLD signal as accepted by the process in wait(), with the same meaning of ‘‘accepted’’ as when that word is applied to the sigwait ( ) family of functions.

Allowing the wait() family of functions to behave this way permits an implementation to be able to deal precisely with SIGCHLD signals.

In particular, an implementation that does accept (discard) the SIGCHLD signal can make the following guarantees regardless of the queuing depth of signals in general (the list of waitable children can hold the SIGCHLD queue):

  1. If a SIGCHLD signal handler is established via sigaction() without the SA_RESETHAND flag, SIGCHLD signals can be accurately counted; that is, exactly one SIGCHLD signal will be delivered to or accepted by the process for every child process that terminates.
  2. A single wait() issued from a SIGCHLD signal handler can be guaranteed to return immediately with status information for a child process.
  3. When SASIGINFO is requested, the SIGCHLD signal handler can be guaranteed **to receive a non-null pointer to a `siginfo_t\' structure that describes a child process for which a wait via waitpid ( ) or waitid ( ) will not block or fail.**
  4. The system() function will not cause the SIGCHLD handler of a process to be called as a result of the fork()/exec executed within system() because system() will accept the SIGCHLD signal when it performs a waitpid() for its child process. This is a desirable behavior of system() so that it can be used in a library without causing side-effects to the application linked with the library.

An implementation that does not permit the wait() family of functions to accept (discard) a pending SIGCHLD signal associated with a successfully waited-for child, cannot make the guarantees described above for the following reasons:

  • Guarantee #1
    Although it might be assumed that reliable queuing of all SIGCHLD signals generated by the system can make this guarantee, the counter-example is the case of a process that blocks SIGCHLD and performs an indefinite loop of fork()/wait() operations. If the implementation supports queued signals, then eventually the system will run out of memory for the queue. The guarantee cannot be made because there must be some limit to the depth of queuing.

  • Guarantees #2 and #3
    These cannot be guaranteed unless the wait() family of functions accepts the SIGCHLD signal. Otherwise, a fork()/wait() executed while SIGCHLD is blocked (as in the system() function) will result in an invocation of the handler when SIGCHLD is unblocked, after the process has disappeared.

  • Guarantee #4
    Although possible to make this guarantee, system() would have to set the SIGCHLD handler to SIG_DFL so that the SIGCHLD signal generated by its fork() would be discarded (the SIGCHLD default action is to be ignored), then restore it to its previous setting. This would have the undesirable side-effect of discarding all SIGCHLD signals pending to the process.

Underlines

sigaction()

  • DESCRIPTION

    The storage occupied by sa_handler and sa_sigaction may overlap, and a conforming application shall not use both simultaneously.

    • sa_flags

      SA_NOCLDWAIT

      If set, and sig equals SIGCHLD, child processes of the calling processes shall not be transformed into zombie processes when they terminate. If the calling process subsequently waits for its children, and the process has no unwaited-for children that were transformed into zombie processes, it shall block until all of its children terminate, and wait(), waitid(), and waitpid() shall fail and set errno to [ECHILD]. Otherwise, terminating child processes shall be transformed into zombie processes, unless SIGCHLD is set to SIG_IGN.

wait()

  • APPLICATION USAGE

    Calls to wait() will collect information about any child process. This may result in interactions with other interfaces that may be waiting for their own children (such as by use of system ( )). For this and other reasons it is recommended that portable applications not use wait(), but instead use waitpid ( ). For these same reasons, the use of waitpid ( ) with a pid argument of −1, and the use of waitid() with the idtype argument set to P_ALL, are also not recommended for portable applications.

  • RATIONALE

    The waitpid ( ) function is provided for three reasons:

    1. To support job control

    2. To permit a non-blocking version of the wait ( ) function

    3. To permit a library routine, such as system() or pclose(), to wait for its children without interfering with other terminated children for which the process has not waited

      Allowing the wait() family of functions to discard a pending SIGCHLD signal that is associated with a successfully waited-for child process puts them into the sigwait() and sigwaitinfo() category with respect to SIGCHLD.

      This definition allows implementations to treat a pending SIGCHLD signal as accepted by the process in wait(), with the same meaning of ‘‘accepted’’ as when that word is applied to the sigwait ( ) family of functions.

      Allowing the wait() family of functions to behave this way permits an implementation to be able to deal precisely with SIGCHLD signals.

      In particular, an implementation that does accept (discard) the SIGCHLD signal can make the following guarantees regardless of the queuing depth of signals in general (the list of waitable children can hold the SIGCHLD queue):

    4. If a SIGCHLD signal handler is established via sigaction() without the SA_RESETHAND flag, SIGCHLD signals can be accurately counted; that is, exactly one SIGCHLD signal will be delivered to or accepted by the process for every child process that terminates.

    5. A single wait() issued from a SIGCHLD signal handler can be guaranteed to return immediately with status information for a child process.

    6. When SASIGINFO is requested, the SIGCHLD signal handler can be guaranteed **to receive a non-null pointer to a `siginfo_t\' structure that describes a child process for which a wait via waitpid ( ) or waitid ( ) will not block or fail.**

<u>An implementation that does not permit the *wait*() family of functions to accept (discard) a pending SIGCHLD signal associated with a successfully waited-for child, **cannot make the guarantees described above for the following reasons:**</u>

- Guarantee #1
     Although it might be assumed that reliable queuing of all SIGCHLD signals generated by the system can make this guarantee, the counter-example is the case of a process that blocks SIGCHLD and performs an indefinite loop of *fork*()/*wait*() operations. <u>**If the implementation supports queued signals, then eventually the system will run out of memory for the queue. The guarantee cannot be made because there must be some limit to the depth of queuing.**</u>

- Guarantees #2 and #3
    These cannot be guaranteed unless the *wait*() family of functions accepts the SIGCHLD signal. Otherwise, <u>**a *fork*()/*wait*() executed while SIGCHLD is blocked (as in the *system*() function) will result in an invocation of the handler when SIGCHLD is unblocked, after the process has disappeared.**</u>

Summary

  1. wait() 보다 waitpid() 사용을 권한다.

  2. wait은 direct children의 status만 확인 가능하다. grandchildren status를 확인하기 위한 확장판을 만드는 것에 제약은 없다만, 그렇지 않은 경우 direct children status 값만 확인 할 수 있다.

  3. Allowing the wait() family of functions to discard a pending SIGCHLD signal that is associated with a successfully waited-for child process puts them into the sigwait() and sigwaitinfo() category with respect to SIGCHLD..

    => wait에서 SIGCHLD를 무시할 때 wait family 기능을 sigwait(), sigwaitinfo()의 범주 안에서 취급할 수 있다. 해석: sig- 함수 안에서 사용할 수 있다? sig-()함수를 wait함수처럼 사용할 수 있다?

  4. Underlines, summary,3 과 같이 사용하면 SIGCHLD를 보다 정확하게 처리할 수 있다.

  5. 시그널 큐의 깊이와 관계없이 보증되는 것들( => 처리되지 못한 시그널이 얼마나 많이 쌓였는가와 무관하게)

    • #1 SIGCHLD 시그널 핸들러가 sigaction()에 SA_RESETHAND flag, SIGCHLD signals 플래그 없이 설정되면 종료되는 모든 자식 프로세스로 부터 각각 하나의 시그널이 전달될 것이다.

    • #2 SIGCHLD signal handler 내에 단독으로 호출된 wait()은 status를 즉시 반환할 것 이다.

    • #3 SA_SIGINFO 가 요청되면, SIGCHLD signal handler는

  6. Underlines, summary,3 과 같이 사용하지 않으면 몇 가지 이유로 보증할 수 없게된다.

    • 시스템은 신뢰할 만한 시그널 큐잉을 제공하도록 가정되어있다하지만, 반례는 fork/wait의 무한루프 이다. 시그널큐잉을 제공하려면 결국에는 큐의 메모리 크기를 제한할 것 이므로 큐의 깊이에는 제한이 있을 수 밖에 없다.

      => sighandler를 호출하면 큐가 쌓이지 않고 함수를 그때 그때 호출하므로 SIGCHLD를 보다 정확하게 처리할 수 있다

    • fork/wait 실행될 때 SIGCHLD가 blocked에서 unblocked되면 프로세스는 사라진 상태에서 handler가 호출된다.

Reference

[1] The Open Group Base Specifications Issue 7, 2018 Edition, IEEE Std 1003.1-2017 , Oct. 24, 2021. [Online]. Available: https://pubs.opengroup.org/onlinepubs/9699919799/

profile
🏃🏾

0개의 댓글