Note: This has been in my drafts for a while, but I think it’s ready for publishing..
At 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";
Two years later…. Thanks!!!!
Hi,
I just stumbled upon your site looking for a solution to the issue that you mentioned that I have been scratching my hair over. My frustration is being unable to get an exit code from the qx() command that I execute in the child process. It seems that whatever way I use $SIG{CHLD} I still have $? = -1 in my child process. Basically, I am sending an ajax request to a cgi script that has to send the response right away but continue running a longer process in the background. I also need to get both the error code and the msg from qx() in the child process. I am doing this:
$SIG{CHLD} = ‘IGNORE’; # or = \&REAPER
$pid = fork();
if($pid == 0)
{
close STDOUT; # So that the parent sends the response to the client right away.
qx(tar up big directories over 50G…); # This can go on for a few minutes.
if($? ==0) { Send a ‘success’ email } # $? (SOB) is always -1 (Sorry !)
else { Send a ‘failure’ email }
}
Any clues where to fix?