PHP分叉和多个子信号

时间:2010-02-16 06:54:52

标签: php process fork

我正在尝试编写一个脚本,该脚本使用pcntl_* functions创建了许多分叉的子进程。

基本上,有一个脚本在循环中运行大约一分钟,定期轮询数据库以查看是否有要运行的任务。如果有的话,它应该在一个单独的进程中分叉并运行任务,这样父进程就不会被长时间运行的任务阻止。

由于可能有大量任务准备好运行,我想限制创建的子进程数。因此,我通过在每次创建变量时递增变量来跟踪进程数(然后暂停,如果有太多),然后在信号处理程序中递减它。有点像这样:

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;
}

除了两个或多个进程同时完成时,大多数情况下都可以正常工作 - 信号处理函数只调用一次,这会抛出我的计数器。 notes of the PHP manual

中的“匿名”解释了这个原因
  

多个子节点返回的时间少于在给定时刻退出的子节点数SIGCHLD信号是Unix(POSIX)系统的正常行为。 SIGCHLD可能被解读为“一个或多个孩子改变了状态 - 去检查你的孩子并收集他们的状态值”。

我的问题是:如何检查孩子并收集他们的状态?有没有可靠的方法来检查在任何给定时间打开多少子进程?

使用PHP 5.2.9

3 个答案:

答案 0 :(得分:2)

一种方法是保留子进程的PID数组,并在信号处理程序中检查每个PID以查看它是否仍在运行。 (未经测试的)代码如下所示:

define(ticks = 1);

$openProcesses = 0; 
$procs = array();
$max = 3;

pcntl_signal(SIGCHLD, "childFinished");

while (!time_is_up()) {
    if (there_is_something_to_do()) {
        $pid = pcntl_fork();
        if (!$pid) {      
            foo();        
            exit(0);      
        } else {          

            $procs[] = $pid; // add the PID to the list

            ++$openProcesses;
            if ($openProcesses >= $max) {
                pcntl_wait($status);     
            }                           
        }
    } else {
        sleep(3);
    }
}

function childFinished($signo) {

    global $openProcesses, $procs;

    // Check each process to see if it's still running
    // If not, remove it and decrement the count
    foreach ($procs as $key => $pid) if (posix_getpgid($pid) === false) {
        unset($procs[$key]);
        $openProcesses--;
    }

}

答案 1 :(得分:0)

您可以让孩子在启动时向父母发送SIGUSR1,然后在退出之前发送SIGUSR2。使用原始信号时要处理的另一件事是内核合并它们,它与RT信号无关。从理论上讲,任何非rt信号都可以合并。

您可以使用sqlite实现某种简单的锁定,其中一次只有一个孩子可以使用说话棒。只要确保孩子们正常处理致命的信号,这样他们就可以保持活着以释放锁定。

答案 2 :(得分:0)

我知道这已经晚了8年(希望您能找到答案),但以防万一它对我要回答的其他人有所帮助。

在这里使用pcntl_w *函数将是您的朋友,并且您可能希望实现过程收割者。该文档不是很有帮助,仍然没有包含任何有用的示例。

这将是一个多部分的过程:

1-使用pcntl_signal将捕获的信号发送到您的信号处理程序

2-在该循环内进行循环/轮询;

3-遍历孩子的数组(将在下面创建)并根据需要收获

4-fork():这将包含以下内容:

pcntl_async_signals(true);

$children = array();
while ($looping === true)
{
    reapChildren();
    if (($pid = pcntl_fork()) exit (1); // error
    elseif ($pid) // parent
    { 
        $children[] = $pid;
        // close files/sockets/etc
        posix_setpgid ($pid,posix_getpgrp());
    }
    else
    { // child
        posix_setpgid(posix_getpid(),posix_getppid());
        // ... jump to child function/object/code/etc ...
        exit (0); // or whatever code you want to return
    }
} // end of loop

在收割机中,您将需要以下内容:

function reapChildren()
{
    global $children;
    foreach ($children as $idx => $pid)
    {
        $rUsage = array();
        $status = 0; // integer which will be used as the $status pointer
        $ret = pcntl_waitpid($pid, $status, WNOHANG|WUNTRACED, $rUsage);
        if (pcntl_wifexited($status)) // the child exited normally
        {
            $exitCode = pcntl_wexitstatus($status); // returns the child exit status
        }
        if (pcntl_wifsignaled($status))  // the child received a signal
        {
            $signal = pcntl_wtermsig($status); // returns the signal that abended the child
        }
        if (pcntl_wifstopped($status))
        {
            $signal = pcntl_wstopsig($status); // returns the signal that  stopped the child
        }
    }
}

上面的收割者代码将允许您轮询孩子的状态,如果您使用的是php7 +,则在信号处理程序中填充的$ signalInfo数组将包含许多可以使用的有用信息。var_dump .. 一探究竟。另外,在php7 +中使用pcntl_async_signals(true)取代了对clarify(ticks = 1)的需要,并手动调用pcntl_signal_dispatch();

我希望这会有所帮助。