不调用PHP pcntl_signal回调

时间:2012-10-04 08:01:12

标签: php signals posix pcntl

这是完全可重现的代码。

<?php
class console{
    public static function log($msg, $arr=array()){
        $str = vsprintf($msg, $arr);
        fprintf(STDERR, "$str\n");
    }
}
function cleanup(){
    echo "cleaning up\n";
}
function signal_handler($signo){
    console::log("Caught a signal %d", array($signo));
    switch ($signo) {
        case SIGTERM:
            // handle shutdown tasks
             cleanup();
            break;
        case SIGHUP:
            // handle restart tasks
            cleanup();
            break;
        default:
            fprintf(STDERR, "Unknown signal ". $signo);
    }
}

if(version_compare(PHP_VERSION, "5.3.0", '<')){
    // tick use required as of PHP 4.3.0
    declare(ticks = 1);
}

pcntl_signal(SIGTERM, "signal_handler");
pcntl_signal(SIGHUP, "signal_handler");

if(version_compare(PHP_VERSION, "5.3.0", '>=')){
    pcntl_signal_dispatch();
    console::log("Signal dispatched");
}

echo "Running an infinite loop\n";
while(true){
    sleep(1);
    echo date(DATE_ATOM). "\n";
}

当我运行它时,我会每秒看到日期值。现在,如果我按Ctrl + C cleanup函数未被调用。实际上signal_handler未被调用。

以下是示例输出。

$ php testsignal.php 
Signal dispatched
Running an infinite loop
2012-10-04T13:54:22+06:00
2012-10-04T13:54:23+06:00
2012-10-04T13:54:24+06:00
2012-10-04T13:54:25+06:00
2012-10-04T13:54:26+06:00
2012-10-04T13:54:27+06:00
^C

3 个答案:

答案 0 :(得分:9)

CTRL+C触发SIGINT,您尚未安装处理程序:

<?php
...
function signal_handler($signo){
...
    case SIGINT:
        // handle restart tasks
        cleanup();
        break;
...
}
...
pcntl_signal(SIGINT, "signal_handler");
...

现在可行:

$ php testsignal.php
Signal dispatched
Running an infinite loop
2012-10-08T09:57:51+02:00
2012-10-08T09:57:52+02:00
^CCaught a signal 2
cleaning up
2012-10-08T09:57:52+02:00
2012-10-08T09:57:53+02:00
^\Quit

答案 1 :(得分:4)

了解declare的工作原理非常重要:http://www.php.net/manual/en/control-structures.declare.php

它需要包装它在“root”脚本中声明的影响(index.php或类似的东西)。

有趣的事实; declare(ticks = 1);周围的IF检查无关紧要,如果删除这3行,脚本将停止工作,如果将检查更改为if (false)它将起作用,则声明似乎​​在正常代码流程xD

答案 2 :(得分:0)

在Ruben de Vries修正编译时使用声明标记之后,原始代码有两个问题(除了缺少INT信号)。

正如鲁宾所说:

if(false) declare(ticks=1);

此DOES声明要处理的滴答声。试试吧! 文档继续说明如何不使用变量等。这是编译时的黑客攻击。

此外,使用pcntl_signal_dispatch() 直接替换声明标记 - 它必须在代码执行期间调用 - 就像刻度自动执行一样。因此,在脚本头部调用一次只会处理当时等待的任何信号

要使pcntl_signal_dispatch()像使用刻度线一样驱动它,你需要将它全部洒在你的代码上......具体取决于你希望中断生效的地点/时间。

至少:

while(true){
    sleep(1);
    echo date(DATE_ATOM). "\n";
    pcntl_signal_dispatch();
}

因此,原始代码实际上对所有版本的PHP使用滴答,并且另外检查在初始启动过程中接收的一个信号(如果版本大于5.3)。不是OP想要的。

就我个人而言,我发现使用滴答声来驱动PCNTL发出巨大的麻烦。 &#34;循环&#34;在我的情况下是在第三方代码中,所以我不能添加一个调度方法,并且顶级的声明标记并不适用于生产,除非我将它添加到每个文件(某些自动加载,作用域或特定于PHP7版本)问题我认为。)