Perl , $SIG{CHLD} = ‘IGNORE’ , system() and you.
June 24, 2008 – 3:45 pm by Augie SchwerAt first blush you probably would not expect the following to print “-1″; as you probably expect “system” to execute and return the return code from “echo”.
$SIG{CHLD} = 'IGNORE';
print system('echo');
This stumped me too, so I did some research.
From the Perl PIC Documentation:
On most Unix platforms, the CHLD (sometimes also known as CLD) signal has special behavior with respect to a value of ‘IGNORE’. Setting $SIG{CHLD} to ‘IGNORE’ on such a platform has the effect of not creating zombie processes when the parent process fails to wait() on its child processes (i.e. child processes are automatically reaped). Calling wait() with $SIG{CHLD} set to ‘IGNORE’ usually returns -1 on such platforms.
And perldoc -f system:
The return value is the exit status of the program as returned by the wait call. … Return value of -1 indicates a failure to start the program or an error of the wait(2) system call (inspect $! for the reason).
And wait(2):
POSIX.1-2001 specifies that if the disposition of SIGCHLD is set to SIG_IGN or the SA_NOCLDWAIT flag is set for SIGCHLD (see sigaction(2)), then children that terminate do not become zombies and a call to wait() or waitpid() will block until all children have terminated, and then fail with errno set to ECHILD. (The original POSIX standard left the behaviour of setting SIGCHLD to SIG_IGN unspecified.) Linux 2.6 conforms to this specification. However, Linux 2.4 (and earlier) does not: if a wait() or waitpid() call is made while SIGCHLD is being ignored, the call behaves just as though SIGCHLD were not being ignored, that is, the call blocks until the next child terminates and then returns the process ID and status of that child.
So on Linux 2.6 kernels if you auto. reap zombies by setting $SIG{CHLD} to ‘IGNORE’, then you can’t trust the return code from ’system’ as it will always be ‘-1′.
One solution would be to reap your own zombies with one of the popular zombie reaping code snipets.
sub REAPER { 1 until waitpid(-1 , WNOHANG) == -1 };
$SIG{CHLD} = \&REAPER;
print system('echo');
Be careful when using the special Perl variable ‘$?’ because it will have been altered by the call to ‘waitpid’ in your REAPER.
This is because when a child exits a SIG_CHLD is sent to its parent to see this in action try the code below:
sub REAPER
{ print "REAPER\n"; waitpid(-1 , WNOHANG); }
$SIG{CHLD} = \&REAPER;
system('echo');
print "RV: $?\n";







4 Responses to “Perl , $SIG{CHLD} = ‘IGNORE’ , system() and you.”
Thought it would be apropos to mention ye olde “double-fork trick”:
http://ponzo.net/double-fork-trick.pl.txt
-Scott
By Scott Doty on Jun 25, 2008
Building a proper reaper is actually a bit harder than it looks, due primarily to the massive amount of disinformation present on the Internet. For instance, the following code:
sub REAPER { 1 until waitpid(-1 , WNOHANG) == -1 };
Can actually infinitely loop in some circumstances, as waitpid() can return 0. The exact circumstances of this do vary between systems, so check your documentation.
My reapers typically look like:
sub REAPER { while(waitpid(-1, WNOHANG) > 0) {} };
One other note. The seemingly correct:
sub REAP {
while (1)
{
my $pid = waitpid(-1, &WNOHANG);
last if ($pid == 0 || $pid == -1);
$fork_count–;
}
}
Is actually incorrect, assuming $fork_count is being used to track the number of forks that a process has in service, for instance to prevent fork bombs. SIGCHLD can be generated not only only when a child exits, but also when a child process is stopped (e.g. through SIGSTOP or the like). WIFEXITED() in the POSIX library allows you to check if a process _really_ exited, or if it’s just fooling.
By Nathan Patrick on Jun 26, 2008
Perl , $SIG{CHLD} = ‘IGNORE’ , system() and you.”
Since this $SIG{CHLD} = ‘IGNORE’ or \&REAPER puts -1 in $?, how do you determine if all the socket reads, writes and system calls are successful or not?
By Alan Mah on Dec 15, 2008
@Alan, I believe the answer is reap your own zombies and save the value of $? before calling waidpid :
#!/usr/bin/perl
use strict;
use warnings;
use POSIX;
my $RV;
sub REAPER
{ print "REAPER\n"; $RV=$?; waitpid(-1 , WNOHANG); }
$SIG{CHLD} = \&REAPER;
system('true');
print "RV: $?\n";
print "RV: $RV\n";
By Augie Schwer on Dec 17, 2008