It turns out that we can prevent appearing of a zombie process (i.e. the one whose parent doesn't wait()
for it to _exit()
) by specifying SIGCHLD
signal to be ignored with sigaction()
by its parent. However, it seems like SIGCHLD
is ignored by default anyway. How come does this work?
int main (void) {
struct sigaction sa;
sa.sa_handler = SIG_IGN; //handle signal by ignoring
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGCHLD, &sa, 0) == -1) {
perror(0);
exit(1);
}
int pid = fork();
if (pid == 0) { //child process
_exit(0);
}
do_something(); //parent process
return 0;
}
The default behavior of SIGCHLD
is to discard the signal, but the child process is kept as a zombie until the parent calls wait()
(or a variant) to get its termination status.
But if you explicitly call sigaction()
with the disposition SIG_IGN
, that causes it not to turn the child into a zombie -- when the child exits it is reaped immediately. See https://stackoverflow.com/a/7171836/1491895
The POSIX way to get this behavior is by calling sigaction
with handler = SIG_DFL
and flags
containing SA_NOCLDWAIT
. This is in Linux since 2.6.
Does this mean that discarding signal and ignoring signal are two different behaviors?
What's special is ignoring it explicitly by calling
sigaction()
. It doesn't change how the signal is treated in your process, but it changes what happens to the process that exits.@igor Yes, discarding a signal and ignoring it are different things. Discarding means the signal is basically just thrown away, while ignoring means it's actually considered delivered, but there is no handler or any other special action performed.
@twalberg But except in this special case, there's no behavioral difference between them.
@Barmar That may be true in this particular case, but it's not necessarily true across all the different signals. Thus, it can be important to understand the difference between throwing away a signal and delivering it but not doing anything in response...