views:

70

answers:

2

I'm trying to write a script which creates a number of forked child processes using the pcntl_* functions.

Basically, there is a single script which runs in a loop for about a minute, periodically polling a database to see if there is a task to be run. If there is one, it should fork and run the task in a separate process so that the parent isn't held up by a long-running task.

Since there possibly could be a large number of tasks ready to be run, I want to limit the number of child processes that are created. Therefore, I am keeping track of the number of processes by incrementing a variable each time one is created (and then pausing if there's too many), and then decrementing it in a signal handler. Kind of like this:

define(ticks = 1);

$openProcesses = 0; // how many we have open
$max = 3;           // the most we want open at a time

pcntl_signal(SIGCHLD, "childFinished");

while (!time_is_up()) {
    if (there_is_something_to_do()) {
        $pid = pcntl_fork();
        if (!$pid) {      // I am the child
            foo();        //   run the long-running task
            exit(0);      //   and exit
        } else {          // I am the parent
            ++$openProcesses;
            if ($openProcesses >= $max) {
                pcntl_wait($status);    // wait for any child to exit 
            }                           // before continuing
        }
    } else {
        sleep(3);
    }
}

function childFinished($signo) {
    global $openProcesses;
    --$openProcesses;
}

This works pretty much ok most of the time, except for when two or more processes finish simultaneously - the signal handler function is only called once, which throws out my counter. The reason for this is explained by "Anonymous" in the notes of the PHP manual:

Multiple children return less than the number of children exiting at a given moment SIGCHLD signals is normal behavior for Unix (POSIX) systems. SIGCHLD might be read as "one or more children changed status -- go examine your children and harvest their status values".

My question is this: How do I examine the children and harvest their status? Is there any reliable way to check how many child processes are open at any given time?

Using PHP 5.2.9

A: 

You could have children send a SIGUSR1 to the parent when they start,then a SIGUSR2 before they exit. The other thing you are dealing with when using primitive signals is the kernel merging them, which it does not do with RT signals. In theory, ANY non-rt signal could be merged.

You might implement some kind of simple locking using sqlite, where only one child at a time can have the talking stick. Just make sure that children handle normally fatal signals so that they remain alive to free the lock.

Tim Post
+1  A: 

Not sure if you have seen this, but this is a really interesting idea and thought this might help

http://stackoverflow.com/questions/1160192/monitoring-children-forked-using-php

WarmWaffles