如果您正在以 CLI 和“守护进程”模式(即在循环中)运行 PHP,则必须在每个循环中调用此函数以检查是否有新的信号正在等待分派。
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
pcntl_signal_dispatch — 调用待处理信号的信号处理程序
函数 pcntl_signal_dispatch() 会调用由 pcntl_signal() 安装的信号处理程序,以处理每个待处理的信号。
此函数没有参数。
范例 #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 中执行的操作,直到队列为空。
嗯,我之前说错了。在信号处理程序之外处理信号是不够的。它们必须在滴答处理程序(显式或隐式)之外进行处理。所以...
注册一个调用 pcntl_signal_dispatch() 的滴答处理程序;
在信号处理程序中,将您的信号入队;
在脚本的主循环中,处理您的信号;
<?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("派生了 $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);
}
?>