I have been having trouble reaping my child process. It seemed most of the time, children were reaped properly but *sometimes* they would stay as zombies. I was catching the CHLD signal to reap child processes with the following code:
<?php
function childFinished($signal)
{
global $kids;
$kids--;
pcntl_waitpid(-1, $status);
}
$kids = 0;
pcntl_signal(SIGCHLD, "childFinished");
for ($i = 0; $i < 1000; $i++)
{
while ($kids >= 50) sleep(1);
$pid = pcntl_fork();
if ($pid == -1) die('failed to fork :(');
if ($pid == 0)
{
exit(0);
}
else { $kids++; }
}
print "Reaping $kids children...\n";
while ($kids) sleep(1);
print "Finished.\n";
?>
The problem was, $kids never became zero so it would effectively wait forever. After wracking my brains (UNIX forks are new to me) I finally read the Perl IPC docs and viola, a solution! It turns out that because signal handlers are not re-entrant, my handler will not be called again while it is in use. The scenario that caused me trouble was that one child would exit and call the signal handler, which would pcntl_waitpid() it and decrement the counter. Meanwhile, another child would exit while the first child was still being reaped, so the second would never get to notify the parent!
The solution was to continually reap children from the SIGCHLD handler, so long as there were children to reap. Here is the *fixed* childFinished function:
<?php
function childFinished($signal)
{
global $kids;
while( pcntl_waitpid(-1, $status, WNOHANG) > 0 )
$kids--;
}
?>