Theory
POSIX says about SIG_IGN
(strictly under the XSI extension note):
If the action for the SIGCHLD signal is set to SIG_IGN, 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].
And in the description of <signal.h>
says that the default signal disposition for SIGCHLD
is SIG_IGN
'ignore' (code 'I'). However, it is the SIG_DFL
behaviour is to 'ignore' the signal; the signal is never generated. This is different from the SIG_IGN
behaviour.
So, you don't have to set a signal handler, but you shouldn't get information about the process back — you should just get an error once there are no children left.
Demonstration Code
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static siginfo_t sig_info;
static volatile sig_atomic_t sig_num;
static void *sig_ctxt;
static void catcher(int signum, siginfo_t *info, void *vp)
{
sig_num = signum;
sig_info = *info;
sig_ctxt = vp;
}
static void set_handler(int signum)
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = catcher;
sigemptyset(&sa.sa_mask);
if (sigaction(signum, &sa, 0) != 0)
{
int errnum = errno;
fprintf(stderr, "Failed to set signal handler (%d: %s)
", errnum, strerror(errnum));
exit(1);
}
}
static void prt_interrupt(FILE *fp)
{
if (sig_num != 0)
{
fprintf(fp, "Signal %d from PID %d
", sig_info.si_signo, (int)sig_info.si_pid);
sig_num = 0;
}
}
static void five_kids(void)
{
for (int i = 0; i < 5; i++)
{
pid_t pid = fork();
if (pid < 0)
break;
else if (pid == 0)
{
printf("PID %d - exiting with status %d
", (int)getpid(), i);
exit(i);
}
else
{
int status = 0;
pid_t corpse = wait(&status);
printf("Child: %d; Corpse: %d; Status = 0x%.4X
", pid, corpse, (status & 0xFFFF));
prt_interrupt(stdout);
fflush(0);
}
}
}
int main(void)
{
printf("SIGCHLD set to SIG_IGN
");
signal(SIGCHLD, SIG_IGN);
five_kids();
printf("SIGCHLD set to catcher()
");
set_handler(SIGCHLD);
five_kids();
return(0);
}
The fflush(0);
call ensures standard output (in particular) is flushed, which matters if the output of the sample program is piped to another program.
Example Output
The output from the example code agrees with the theory — but requires a little interpretation.
SIGCHLD set to SIG_IGN
PID 4186 - exiting with status 0
SIGCHLD set to SIG_IGN
Child: 4186; Corpse: -1; Status = 0x0000
PID 4187 - exiting with status 1
Child: 4187; Corpse: -1; Status = 0x0000
PID 4188 - exiting with status 2
Child: 4188; Corpse: -1; Status = 0x0000
PID 4189 - exiting with status 3
Child: 4189; Corpse: -1; Status = 0x0000
PID 4190 - exiting with status 4
Child: 4190; Corpse: -1; Status = 0x0000
SIGCHLD set to catcher()
PID 4191 - exiting with status 0
SIGCHLD set to catcher()
Child: 4191; Corpse: -1; Status = 0x0000
Signal 20 from PID 4191
Child: 4192; Corpse: 4191; Status = 0x0000
PID 4192 - exiting with status 1
Child: 4193; Corpse: 4192; Status = 0x0100
Signal 20 from PID 4192
PID 4193 - exiting with status 2
Child: 4194; Corpse: 4193; Status = 0x0200
Signal 20 from PID 4193
PID 4194 - exiting with status 3
Child: 4195; Corpse: 4194; Status = 0x0300
Signal 20 from PID 4194
PID 4195 - exiting with status 4
The first section of the output agrees exactly with the theory; the calling code gets no information about the dead children except that there are no dead children (left) to wait for.
The second section of the output agrees with the theory too, but it appears that the children aren't executing before the parent, so the first wait()
from the parent has no dead children to wait for, and it returns -1. Subsequent calls get the various children, but one escapes unwaited for (but the corpse will be cleaned up by the system). If the code used waitpid()
such as: pid_t corpse = waitpid(pid, &status, 0);
, it would wait for each child in turn.
Some of this commentary may need revision in the light of the modified comments about 'ignore' above. TBD — out of time right now.
Mac OS X 10.8.4 on 3 GHz Intel Core 2 Duo, GCC 4.6.0, 64-bit compilation:
gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes
-Wold-style-definition sigchld.c -o sigchld
This was a quick adaptation of some existing code to test the behaviour of SIGINT
and pause()
. It may have some superfluous material in it. You do not have to call wait()
or one of its relatives in the signal handler; you may do so if you wish, though.