如果设置了pcntl_signal(),我可以依赖在 SIGTERM 上调用register_shutdown_function()吗?

2022-08-30 23:39:45

我正在处理一个定期调用后台进程的应用程序。其中一个被cron调用,但我正在寻找更强大的东西,所以我将其转换为在主管下运行。(它可能会运行 10 分钟,在此期间,它可以检测要完成的工作或空闲状态。退出后,Supervisor 将自动重生一个干净的实例。

由于 Supervisor 更擅长确保只有指定数量的实例并行运行,因此我可以运行它们更长时间。但是,这确实意味着我的进程更有可能直接从接收终止信号,或者因为它们已通过 Supervisor 停止。因此,我正在尝试如何在PHP中处理这个问题。kill

看起来基本的解决方案是像这样使用pcntl_signal():

declare(ticks = 1);
pcntl_signal(SIGTERM, 'signalHandler');
pcntl_signal(SIGINT, 'signalHandler');

function signalHandler($signal) {
    switch($signal) {
        case SIGTERM:
        case SIGINT:
            echo "Exiting now...\n";
            exit();
    }
}

但是,我的代码中有几个点可以仔细处理关闭。一种方法是让一个处理程序调用这些不同的内容,但这需要一些重构,我想避免这种情况。另一种方法是在我需要的地方添加它,但不幸的是,似乎一次只能安装一个处理程序。pcntl_signal()

但是,看起来我可以使用register_shutdown_function()。这本身不会捕获或其他终止 sigs,手册在这一点上非常清楚:^C

如果进程被 SIGTERM 或 SIGKILL 信号杀死,则不会执行关机功能

令人惊讶的是,我发现如果我只做一个出口,那么关闭处理程序确实调用了。此外,由于可以有许多关闭处理程序,这很好地解决了我的问题 - 我的代码中希望优雅地处理终止的每个类都可以捕获和管理自己的关闭。pcntl_signal()

我最初的问题是,为什么这有效?我尝试过在没有信号处理程序的情况下注册关机功能,并且似乎没有像手册所说的那样调用它。我猜PHP使进程保持活动状态以便处理信号,这会导致调用关闭处理程序?

另外,当手册对它产生怀疑时,我可以依靠这种行为吗?我正在使用PHP 5.5,并且还不想升级到PHP7。因此,我对这是否适用于5.5和5.6以及各种发行版感兴趣。

当然,无论它是否(或不会)在7.0和/或7.1上运行,也会很有趣 - 我知道尽管在7.1中会以不同的方式处理刻度,因此它有不同的行为的可能性更大。


答案 1

PHP 用户关闭函数是在普通进程终止期间调用的,有点类似于在其他编程语言中注册的函数。脚本末尾的显式或隐式退出是普通的进程终止。atexitexit

然而,信号死亡是异常的进程终止。SIGTERM 的默认行为(以及 SIGKILL 的强制行为)是立即终止正在运行的进程,而不运行任何清理处理程序。

当您使用 拦截 SIGTERM 时,您正在安装不同的行为,并让自己有机会体验普通的进程终止。pcntl_signal

是的,您可以依靠此行为。虽然主要的手动条目不是明确的,但你引用的“注释”的其余部分是:

[Y]ou 可用于为 SIGTERM 安装一个处理程序,该处理程序用于干净利落地结束。pcntl_signal()exit()


答案 2

除了皮尔克劳斯的正确答案:

信号处理程序对于更改信号的行为(即代替默认操作)是必要的。之后发生的任何事情都是无关紧要的。

您可以使用信号处理程序仅执行紧急清理,然后调用强制终止而不进行清理。如果你在这里,通常关闭序列(即调用所有析构函数,关闭处理程序等)被启动。posix_kill(getmypid(), SIGKILL);exit()

当你说除了,你不是严格正确的。您需要改用信号处理程序,并且可能还需要一个关机处理程序。[参考您的评论]

还要注意,在启动关闭序列后调用将中止其当前部分(即,当您当前在关闭处理程序中并被调用时,所有后续的关闭处理程序都将被忽略 - 但例如,仍会调用析构函数)。理想情况下,您可以对此进行一些检查:exit()exit()

register_shutdown_function(function() { $GLOBALS["#__in_shutdown"] = 1; });

并检查周围:exit()

if (empty($GLOBALS["#__in_shutdown"])) {
    exit;
}

以防万一你想真正安全,除非有人开火SIGKILL。

请注意,7.0 或 7.1 不会更改此行为;7.1中的所有变化都是多余的,所表现出的行为仍然是一样的。declare(ticks=1);


推荐