如果设置了 pcntl_signal(),我可以依赖在 SIGTERM 上调用 register_shutdown_function() 吗?
Can I rely on register_shutdown_function() being called on SIGTERM, if pcntl_signal() is set up?
我正在开发一个具有定期调用的后台进程的应用程序。其中一个被 cron 调用,但我正在寻找更强大的东西,所以我将它转换为 运行 在 Supervisor 下。 (它可能会 运行 10 分钟,在此期间它可以检测到要执行的工作或空闲。一旦退出,Supervisor 将自动重新生成一个干净的实例。)
由于 Supervisor 更擅长确保只有指定数量的实例是并行 运行,我可以 运行 更长时间地处理它们。然而,这确实意味着我的进程更有可能直接从 kill
接收到终止信号,或者因为它们已通过 Supervisor 停止。因此,我正在试验如何在 PHP.
中处理这个问题
看起来基本的解决方案是像这样使用 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()
。这不会自行捕获 ^C
或其他终止信号,手册在这一点上非常清楚:
Shutdown functions will not be executed if the process is killed with a SIGTERM or SIGKILL signal
令人惊讶的是,我发现如果我使用 pcntl_signal()
只是执行退出,那么关闭处理程序 确实会被调用 。此外,由于一个人可以有多个关闭处理程序,这很好地解决了我的问题 - 我的代码中每个希望优雅地处理终止的 class 都可以捕获和管理自己的关闭。
我最初的问题是,为什么这样做有效?我试过在没有信号处理程序的情况下注册一个关闭函数,这似乎没有被调用,正如手册所说。我猜 PHP 使进程保持活动状态以处理信号,这会导致调用关闭处理程序?
此外,当手册对它产生怀疑时,我可以依赖这种行为吗?我正在使用 PHP 5.5,目前还不想升级到 PHP7。所以我很想知道这是否适用于 5.5 和 5.6,以及各种发行版。
当然,它是否会(或不会)在 7.0 and/or 7.1 上工作,这也很有趣 - 我知道 ticks will be handled differently 在 7.1 上,所以有更大的机会这有不同的行为。
PHP 用户关闭函数在普通进程终止期间被调用,有点类似于在其他编程语言中使用 atexit
注册的函数。在脚本末尾显式 exit
或隐式退出是普通进程终止。
然而,信号死亡是进程异常终止。 SIGTERM 的默认行为(以及 SIGKILL 的强制行为)是立即终止 运行 进程,没有 运行 任何清理处理程序。
当您使用 pcntl_signal
拦截 SIGTERM 时,您正在安装不同的行为并让您自己有机会体验普通的进程终止。
是的,您可以依赖此行为。虽然主要的手册条目并不明确,但您引用的 "Note" 的其余部分是:
[Y]ou can use pcntl_signal()
to install a handler for a SIGTERM which uses exit()
to end cleanly.
除了pilcrows 正确答案:
信号处理程序是更改信号行为所必需的(即代替默认操作)。之后发生的一切都无关紧要。
您可以使用信号处理程序只进行紧急清理,然后调用 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);
多余的,表现出的行为仍然相同。
我正在开发一个具有定期调用的后台进程的应用程序。其中一个被 cron 调用,但我正在寻找更强大的东西,所以我将它转换为 运行 在 Supervisor 下。 (它可能会 运行 10 分钟,在此期间它可以检测到要执行的工作或空闲。一旦退出,Supervisor 将自动重新生成一个干净的实例。)
由于 Supervisor 更擅长确保只有指定数量的实例是并行 运行,我可以 运行 更长时间地处理它们。然而,这确实意味着我的进程更有可能直接从 kill
接收到终止信号,或者因为它们已通过 Supervisor 停止。因此,我正在试验如何在 PHP.
看起来基本的解决方案是像这样使用 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()
。这不会自行捕获 ^C
或其他终止信号,手册在这一点上非常清楚:
Shutdown functions will not be executed if the process is killed with a SIGTERM or SIGKILL signal
令人惊讶的是,我发现如果我使用 pcntl_signal()
只是执行退出,那么关闭处理程序 确实会被调用 。此外,由于一个人可以有多个关闭处理程序,这很好地解决了我的问题 - 我的代码中每个希望优雅地处理终止的 class 都可以捕获和管理自己的关闭。
我最初的问题是,为什么这样做有效?我试过在没有信号处理程序的情况下注册一个关闭函数,这似乎没有被调用,正如手册所说。我猜 PHP 使进程保持活动状态以处理信号,这会导致调用关闭处理程序?
此外,当手册对它产生怀疑时,我可以依赖这种行为吗?我正在使用 PHP 5.5,目前还不想升级到 PHP7。所以我很想知道这是否适用于 5.5 和 5.6,以及各种发行版。
当然,它是否会(或不会)在 7.0 and/or 7.1 上工作,这也很有趣 - 我知道 ticks will be handled differently 在 7.1 上,所以有更大的机会这有不同的行为。
PHP 用户关闭函数在普通进程终止期间被调用,有点类似于在其他编程语言中使用 atexit
注册的函数。在脚本末尾显式 exit
或隐式退出是普通进程终止。
然而,信号死亡是进程异常终止。 SIGTERM 的默认行为(以及 SIGKILL 的强制行为)是立即终止 运行 进程,没有 运行 任何清理处理程序。
当您使用 pcntl_signal
拦截 SIGTERM 时,您正在安装不同的行为并让您自己有机会体验普通的进程终止。
是的,您可以依赖此行为。虽然主要的手册条目并不明确,但您引用的 "Note" 的其余部分是:
[Y]ou can use
pcntl_signal()
to install a handler for a SIGTERM which usesexit()
to end cleanly.
除了pilcrows 正确答案:
信号处理程序是更改信号行为所必需的(即代替默认操作)。之后发生的一切都无关紧要。
您可以使用信号处理程序只进行紧急清理,然后调用 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);
多余的,表现出的行为仍然相同。