我看pcntl源码中php_pcntl_pending_signal先初始化为32个,丢失信号的场景就是信号队列满了吧,代码如下:
psig = PCNTL_G(spares);
if (!psig) {
/ oops, too many signals for us to track, so we'll forget about this one /
return;
}
如果队列没满的话是可以继续处理的,如果满了,来的信号都丢了,第二次的pcntl_signal_dispatch 是预防那些场景那?相关php代码如图片所示
pcntl_signal_dispatch()
是处理已经收到的信号。假设只有一个
pcntl_signal_dispatch()
,截图里代码简化成如下
正常情况下,进程是阻塞在位置2。
当信号到来时,pcntl_wait()返回,代码继续执行,就执行到了位置1。
执行1程中,这时候有新的信号到来,新的信号放入信号队列不做处理(因为需要再次调用
pcntl_signal_dispatch()
才能被处理)。执行完1后代码继续执行2,然后一直阻塞在2的位置,新来的信号没有被处理(这里其实意思是没被处理,信号其实没丢)。
所以在workerman里又调用了一次
pcntl_signal_dispatch()
,这样能够尽量避免漏掉信号。以下是丢失信号的一个demo,你可以自己测试下。
test.php
运行 php test.php,然后在另外一个终端上给这个进程发送信号
kill -SIGUSR1 pid
,如果只有一个pcntl_signal_dispatch();
,你会看到只打印一个GET SIGNAL
。但是实际上代码执行过程中一共收到了2个信号。有一个信号看起来丢失了。
嗯,感谢,另外,如果系统调用有超时设置的话(例如select)并且在退出系统调用的时候不会退出while循环的情况下,其实一次也够了
必须2个,靠超时触发的话有延迟。
@1:老大问下,同时过来2个信号,为啥sleep 10000下面的pcntl_signal_dispatch也能运行,sleep对它不起作用吗?
信号会打断sleep
@walkor:就像你说的两个也是尽量避免漏掉,如果在执行第二个的时候又来了个信号,但是执行完之后会退出循环,一样会有信号漏掉,当然这是极端的情况。我说的那种情况就像Events/select中的loop一样(当然也有可能漏掉),就像如下代码:
对,都是尽量避免漏掉。loop这里考虑性能问题,只使用了一个pcntl_signal_dispatch();