Search code examples
perlunixsignalssignal-handlingsigchld

What's the difference between various $SIG{CHLD} values?


What is the difference between these settings?

$SIG{CHLD} = 'IGNORE'  
$SIG{CHLD} = 'DEFAULT'  
$SIG{CHLD} = ''  
$SIG{CHLD} = undef

According to "Advanced Programming in the UNIX Environment, 2nd edition", figure 10.1 the default value of SIGCHLD is "ignore."

If "ignore" meant "SIG_IGN", then no child would ever be a zombie, and that's not the case.

It doesn't get much more clear from there:

If the process specifically sets its disposition to SIG_IGN, children of the calling process will not generate zombie processes. Note that this is different from its default action (SIG_DFL), which from Figure 10.1 is to be ignored. Instead, on termination, the status of these child processes is discarded.

I'm having a hard time groking what the impact of the various values (or undefined non-value) are. So far, the solution has been to rotate through those choices until I get the desired behavior, and I'd rather understand exactly how each value defines the behavior of the signal.

The behavior: a child process is calling "system" or using backticks which create another child, and the signal would normally be caught by the wrong (parent) handler. Setting a local handler can work, but I don't understand which value is most appropriate if I want the signal from the grand-child to do nothing.

Could someone please illuminate me?

UPDATE: Based on ikegami's feedback, I did some specific testing. The behavior is, at least partially, platform specific.

Consider the following fragment:

$SIG{CHLD} = sub {
    while( ( my $child = waitpid( -1, &WNOHANG ) ) > 0 ) {
        print "SIGNAL CHLD $child\n";
    }
};

my $pid = fork();

if( ! $pid ) {
    system( 'echo Grandchild PID = $$' );
    sleep 2;
    exit;
}

print "Child PID = $pid\n";
sleep 5;

Perl 5.8.6 on Solaris 10 will display "SIGNAL CHLD" messages for the PID of the system() call. Doing anything, even as trivial as

local $SIG{CHLD};

in the child will suppress those messages.

On every other flavor I tried, the reaper never sees the child.


Solution

  • See %SIG.

    $SIG{CHLD} = 'IGNORE'; causes your process to ignore SIGCHLD signals.

    $SIG{CHLD} = 'DEFAULT'; causes your process to treat SIGCHLD signals as it would had you not messed with $SIG{CHLD} or equivalent. According to kill(1), a process on my system ignores SIGCHLD by default

    $SIG{CHLD} = ''; and $SIG{CHLD} = undef; are not valid values.

    As for reaping, children of a parent whose SIGCHLD handler is explicitely set to IGNORE will be reaped automatically by the system as soon as it exits.

    $ perl -e'
       my $pid = fork;
       if (!$pid) { sleep(1); exit; }
       sleep(2);
       system "ps -o pid,stat,command $pid";
    '
      PID STAT COMMAND
    14667 ZN+  [perl] <defunct>
    
    $ perl -e'
       $SIG{CHLD}="IGNORE";
       my $pid = fork;
       if (!$pid) { sleep(1); exit; }
       sleep(2);
       system "ps -o pid,stat,command $pid";
    '
      PID STAT COMMAND
    
    $