[Take II] On SIGCHLD signal semantics

Marc Aurele La France (Marc.La-France@ualberta.ca)
Thu, 27 Jun 1996 10:09:21 -0600 (MDT)


Good day.

This is the readme file for tsi.sigchld.diff.gz which I have uploaded to
ftp://ftp.lmh.ox.ac.uk. This is essentially a copy of the
Documentation/sigchld.txt file that is inserted by this kernel change.

-------------------------------------------------------------------------------

This kernel contains changes to fix Linux's implementation of SystemV SIGCHLD
signal semantics. Specifically, a wait* system call can no longer return the
process id of a child process that exited while its parent's SIGCHLD signal
handler was SIG_IGN. Previously, this was only true if such a child exited
before its parent invoked the wait* system call.

This change will adversely affect programmes that invoke the wait* system call
with an inherited SIGCHLD signal handler of SIG_IGN. Under previous kernels,
such a call would succeed only if a child exited while its parent was already
sleeping in the wait* system call. Otherwise, the call would fail. This meant
the programme's behaviour had a timing dependancy. Under this kernel, a wait*
system call with an inherited SIGCHLD signal handler of SIG_IGN will always
fail. This means programmes that expect BSD-style SIGCHLD signal semantics
(where SIG_IGN is equivalent to SIG_DFL) will fail. As a workaround, POSIX
suggests that applications always set their SIGCHLD signal handler before
calling one of the wait* functions.

Unfortunately, few programmes actually do this. In fact, most BSD utilities
that have been ported to Linux fall into this category. Thus, all such
applications should be changed to run properly under Linux, regardless of the
SIGCHLD signal handler they inherit. A daunting task, to be sure. Rather than
requiring this, a number of alternatives have been proposed in an ongoing
discussion on the linux-kernel mailing list to address, or help address, this
incompatibility between this kernel and BSD. These alternatives modify the
above default behaviour in various ways, and, for discussion purposes, have
been implemented as compilation options. Unless otherwise specified, these
options can be used independently of each other. A description of these
options follows. If you have other suggestions, I'd be happy to hear them!

If POSIX_SIGCHLD is #define'd in kernel/exit.c, the kernel will produce
warnings when an application makes a wait* system call with a SIGCHLD signal
handler it inherited from its parent, or when a child process exits while its
parent process is still using an inherited SIGCHLD signal handler of SIG_IGN.
These warnings are only intended to highlight the problem, and do not, by
themselves, solve it. The warnings are:

Message:
Process name1 (pid num1) waits for its children but ignores their signals
because it inherited this behaviour from name2.

Explanation:
- The name2 programme set its SIGCHLD handler to SIG_IGN.
- The name2 programme invoked the name1 programme through one of the exec*
system calls.
- The name1 programme executed one of the wait* system calls to wait for
one or more of its child processes to terminate.

Remedy:
- The name2 programme probably needs to be changed to reset its SIGCHLD
handler to SIG_DFL before invoking name1.
- The name1 programme probably needs to be changed to set its SIGCHLD to
SIG_DFL before executing the wait* system call.

Message:
Process name1 (pid num1) ignores signals from its children but inherited
this behaviour from name2.

Explanation:
- The name2 programme set its SIGCHLD handler to SIG_IGN.
- The name2 programme invoked the name1 programme through one of the exec*
system calls.
- One of the name1 programme's child processes terminated.

Remedy:
- The name2 programme should probably be changed to reset its SIGCHLD
handler to SIG_DFL before invoking name1.
- The name1 programme probably needs to be changed to initialize its
SIGCHLD handler to either SIG_DFL or SIG_IGN, depending on whether or not
it makes a wait* system call.

Message:
Process name (pid num) waits for its children but does not ensure it
listens to its children.

Explanation:
- The name programme executed a wait* system call without doing anything
about its SIGCHLD handler.

Remedy:
- The name programme should probably be changed to set its SIGCHLD handler
to SIG_DFL before executing the wait* system call. Otherwise, it runs
the risk of inheriting a SIGCHLD handler of SIG_IGN from a buggy parent
process.

To reduce message volume, only one warning is ever produced for the same
process. Also, the third of the above messages is never produced twice in a
row for the same programme (but different processes).

If BSD_SIGCHLD is #define'd in kernel/exit.c, the above SystemV behaviour is
modified such that a wait* system call will never return the process id of a
child process that exited while its parent SIGCHLD signal handler was
*explicitly* set to (rather than inherited as) SIG_IGN. The end result of this
is that applications will be affected by BSD-style SIGCHLD signal semantics
from the time they are exec'ed to the time they set their SIGCHLD signal
handler, after which SystemV semantics take over. The downside of this is that
applications that save and later restore their SIGCHLD signal handler will lose
the inherited aspect of that handler. This last point can somewhat be dealt
with by the next compilation option.

When a programme is exec'ed, this kernel sets a flag to indicate that the
programme's SIGCHLD signal handler was inherited as SIG_IGN. This flag is
normally reset when the programme sets its SIGCHLD signal handler. If
BSD_SIGCHLD is #define'd in kernel/signal.c however, this flag is only reset
when the programme redundantly sets its SIGCHLD signal handler to SIG_IGN.
This means that, if BSD_SIGCHLD is also #define'd in kernel/exit.c, chances are
high(er) that BSD-style SIGCHLD signal semantics are put back in force when the
application restores an inherited SIGCHLD handler of SIG_IGN. The downside
here is that it is unlikely the application is aware that its SIGCHLD signal
semantics are being changed in this fashion. In addition, this mechanism is
not fool-proof.

If BSD_SIGCHLD is defined in fs/exec.c, this kernel will produce a warning when
a programme is about to inherit a SIGCHLD signal handler of SIG_IGN and will
reset this handler to SIG_DFL. Thus SIG_IGN can never be inherited, which
effectively disables the functionality introduced by #define'ing BSD_SIGCHLD in
kernel/exit.c and kernel/signal.c, along with all but the last of the warnings
produced by #define'ing POSIX_SIGCHLD in kernel/exit.c. The message produced
by this option is of the form

Message:
Process name1 (pid n) was prevented from causing name2 to inherit SIG_IGN
as its SIGCHLD signal handler.

Explanation:
The name1 programme was ignoring its children while executing an exec*
system call to invoke the name2 programme.

Kernel action:
The name2 programme will inherit a SIGCHLD signal handler of SIG_DFL.

There are pros and cons in this option also. This is, by far, the most
technically sound solution proposed so far, simply because it minimizes the
number of adversely affected applications. This option breaks those programmes
that depend on inheriting SIG_IGN as a SIGCHLD handler. The only reliable
justification for this dependence that I can think of is the use of an
inherited SIGCHLD signal handler as some kind of on/off parameter, but this
mechanism can be replaced by a more obvious form of parameter passing. The
other downside of this option is that it contravenes POSIX, or, at least, the
letter of POSIX. POSIX.1 leaves as unspecified the behaviour of SIG_IGN as a
SIGCHLD signal handler, and yet, does not disallow (or leave as unspecified)
the inheritance of this behaviour. The rationale section of POSIX.1 tries to
compensate for this by recommending that applications not depend on an
inherited SIGCHLD signal handler, but this simply ignores the large base of
existing programmes that don't implement this recommendation. I suppose one
could argue that leaving the behaviour of SIG_IGN as unspecified also implies
leaving the inheritance of this behaviour as unspecified, but this might only
be wishful thinking on my part. Thus POSIX.1 appears inconsistent in this
respect, if one accepts that the 'P' in 'POSIX' stands for 'Portable'...

Note that just because an application is mentionned in one of the above
messages, it might well be the case that the application does not call one of
the wait system call variants directly. There are also a number of C library
calls, such as system() and pclose(), that implicitly issue a wait* system
call on their caller's behalf.

I will attempt to address in a timely fashion any questions or concerns about
this change that are addressed to Marc Aurele La France <tsi@ualberta.ca>.

-------------------------------------------------------------------------------

.. or to the linux-kernel mailing list.

Marc.

+----------------------------------+-----------------------------------+
| Marc Aurele La France | work: 1-403-492-9310 |
| Computing and Network Services | fax: 1-403-492-1729 |
| 352 General Services Building | email: tsi@ualberta.ca |
| University of Alberta +-----------------------------------+
| Edmonton, Alberta | |
| T6G 2H1 | Standard disclaimers apply |
| CANADA | |
+----------------------------------+-----------------------------------+