2010-02-16 38 views
8

我想写一个脚本,它使用pcntl_* functions创建了一些分叉的子进程。PHP分叉和多个子信号

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

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

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

这工作非常OK的大部分时间,除了当两个或多个进程同时完成 - 信号处理函数只被调用一次,它抛出我的柜台。这样做的原因是“无名氏”在notes of the PHP manual解释说:

多儿童重返不到孩子在某个特定时刻SIGCHLD信号退出的数量为Unix(POSIX)系统的正常行为。 SIGCHLD可能被解读为“一个或多个孩子改变了状态 - 去检查你的孩子并收获他们的状态值”。

我的问题是这样的:我如何检查孩子并获得他们的状态?是否有任何可靠的方法来检查在任何给定时间打开了多少个子进程?

使用PHP 5.2.9

+0

可能只是使用https://www.rabbitmq.com/会使整件事更不容易出错 –

回答

0

你可以有孩子送到一个SIGUSR1父当他们开始,然后SIGUSR2他们退出之前。在使用原始信号时,你正在处理的另一件事是内核合并它们,而这与RT信号无关。理论上,任何非rt信号都可以合并。

你可能会使用sqlite实现某种简单的锁定,一次只有一个孩子可以拥有通话棒。只要确保孩子处理正常的致命信号,以便他们保持活力以释放锁定。

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

}