PHP 8.4.24 Released!

Voting

: five plus zero?
(Example: nine)

The Note You're Voting On

imslushie at hotmaildotcom dot com
19 years ago
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 :(');

  /* child process */
  if ($pid == 0) 
  {
    /* do something */
    exit(0);
  }

  /* parent */
  else { $kids++; }
}

/* when finished, just clean up the children */
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--;
}

?>

<< Back to user notes page

To Top