如果您以 CLI 模式執行 PHP 並作為「守護行程」(例如在迴圈中),則必須在每個迴圈中呼叫此函式以檢查是否有新的訊號等待分派。
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
pcntl_signal_dispatch — 呼叫待處理訊號的訊號處理程式
此函式沒有參數。
範例 #1 pcntl_signal_dispatch() 範例
<?php
echo "安裝訊號處理器...\n";
pcntl_signal(SIGHUP, function($signo) {
echo "訊號處理器被呼叫\n";
});
echo "產生 SIGHUP 訊號給自己...\n";
posix_kill(posix_getpid(), SIGHUP);
echo "分派...\n";
pcntl_signal_dispatch();
echo "完成\n";
?>
上述範例會輸出類似以下的內容:
Installing signal handler... Generating signal SIGHUP to self... Dispatching... signal handler called Done
請注意,從先前 pcntl_signal_dispatch() 呼叫的訊號處理器內呼叫 pcntl_signal_dispatch() 不會觸發任何新的待處理訊號的處理器。這表示如果您編寫一個 CLI 守護行程,它會回應訊號而分支出子行程,那麼這些子行程將無法回應訊號。這讓我頭痛了一陣子,因為發生這種情況時,pcntl_signal_dispatch() 不會引發任何錯誤。一種解決方案是在訊號處理器內設定一個旗標,並在父行程主迴圈的其他地方對其做出反應(透過分支出所需的子行程)。
正如「me at subsonic dot net」所指出的,從先前 pcntl_signal_dispatch() 呼叫的訊號處理器內呼叫 pcntl_signal_dispatch() 不會觸發任何新的待處理訊號的處理器。即使您使用 pcntl_exec() 執行新的 PHP 處理器來執行完全不同的腳本,這似乎也是正確的。
解決方案似乎是在 ticks_handler() 內明確呼叫 pcntl_signal_dispatch()。並使用 sig_handler(int) 作為推播函式到佇列。在 ticks_handler 中呼叫 dispatch 之後,立即將佇列彈出,執行您在 signal_handler 中會執行的操作,直到佇列為空。
嗯,我之前說錯了。只在訊號處理函式外處理訊號是不夠的。它們必須在 tick 處理函式(顯式或隱式)之外處理。所以...
註冊一個呼叫 pcntl_signal_dispatch() 的 tick 處理函式;
在訊號處理函式中,將你的訊號加入佇列;
在你的腳本主迴圈中,處理你的訊號;
<?php
declare(ticks=1);
global $sig_queue;
global $use_queue;
$sig_queue = array();
$use_queue = true; // 設定為 false 以使用舊方法
function tick_handler()
{
pcntl_signal_dispatch();
}
function sig_handler($sig)
{
global $sig_queue;
global $use_queue;
if(isset($use_queue) && $use_queue)
{
$sig_queue[] = $sig;
}
else
{
sig_helper($sig);
}
}
function sig_helper($sig)
{
switch($sig)
{
case SIGHUP:
$pid = pcntl_fork();
if($pid) print("forked $pid\n");
break;
default:
print("未處理的訊號: $sig\n");
}
}
pcntl_signal(SIGHUP, "sig_handler");
while(true)
{
if($use_queue) foreach($sig_queue as $idx=>$sig)
{
sig_helper($sig);
unset($sig_queue[$idx]);
}
sleep(1);
}
?>