After I tested, I found that wait()
was not interrupted by the SIGCHLD` signal to fail and return -1, but returned successfully after executing the signal processing function. Why is that?
Well, if the signal handler ran while the thread was blocked in wait()
then that call was interrupted. I guess the question is why wait()
then went ahead with collecting the child and returned successfully instead of failing with EINTR
.
I can reproduce that behavior. The specifics of how you register the handler are unclear, but in my tests I see the handler running and wait()
thereafter returning successfully even when the SA_RESTART
flag is not set for SIGCHLD
, which is generally a major factor in whether restartable system calls such as wait()
fail with EINTR
when interrupted by a signal.
I'm having trouble locating any documentation that specifically prescribes the observed combination of results for wait()
+ handler function + SIGCHLD
, but the bottom line is that SIGCHLD
is special. In particular, it has a special relationship with wait()
, because the events that a system-generated SIGCHLD
reports on are exactly the ones that a blocking wait()
call is waiting for. Some of the manifestations of that specialness are
The sigaction()
function defines two flags modulating behavior related specifically to SIGCHLD
, and none specific to any other signal.
Even though the default disposition of SIGCHLD
is documented as SIG_IGN
, its actual default behavior is unique to that signal and distinct from the behavior obtained by explicitly setting the disposition to SIG_IGN
.
POSIX has special provisos for the behavior of the wait
-family functions, as described in the notes in the wait()
manual page, about how these functions are affected by the disposition and flags associated with SIGCHLD
.
I don't think either POSIX or Linux explicitly says so, but it all comes around to a pending SIGCHLD
being how the wait
-family functions recognize that there is a child to collect. POSIX is sufficiently unspecific that I think other POSIX systems could do it differently, but to the best of my knowledge, using SIGCHLD
for this purpose is both traditional and what Linux does. Enough so that signal-handling behavior is specifically designed to accommodate the common behavior of using wait()
inside a handler for SIGCHLD
to provide for central processing of terminated children.
It is also notable that wait()
will collect the child and clear the pending SIGCHLD
even if that signal is blocked, analogously to how sigwait()
will receive blocked signals. In that case, any registered handler is bypassed.
Your case of establishing a handler for SIGCHLD
that does not collect the status information for the child is unusual, but consider what needs to happen here:
a SIGCHLD
has been received, it is not blocked, and a signal handler has been registered for it, so the signal handler must run and the SIGCHLD
must be removed from the pending list.
after your particular handler runs, the status information for the child has not yet been consumed, so it must be consumed when control returns to wait()
. Otherwise, it can never be consumed and reported, for receipt of a SIGCHLD
is how the system is triggered to do that, and the context in which the status information is delivered.
I anticipate that your wait()
would fail with either ECHILD
or EINTR
if the signal handler collected the waited-for child via its own wait()
call. Which one depends in part on whether the SA_RESTART
flag is set for SIGCHLD
. I anticipate that it would fail with EINTR
if there was a running child, and the wait()
was interrupted by a synthetic SIGCHLD
, and the SA_RESTART
flag was not set.