2010-02-16 68 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--; 
    } 

}