SIGTERM 的 bash 陷阱失败

SIGTERM 的 bash 陷阱失败

我有一个非常简单的脚本来捕获信号:

#!/bin/bash                                                                     
unlink SigTermRecieved
unlink SigIntRecieved
#trap " touch SigTermRecieved ; exit " SIGTERM                                  
trap " touch SigIntRecieved ; exit " SIGINT
echo "pid is $$"
while true
do
    sleep 60m
done
exit

行为

两个陷阱行均已注释:
SIGTERM 已发送:终止程序。
?从另一个终端窗口发送的 SIGINT 被忽略!
CTRL C 终止程序。

SIGTERM 陷阱未注释:
? SIGTERM 已发送:程序不会终止并且 SIGTERM 陷阱不会执行。
?从另一个终端窗口发送的 SIGINT 被忽略!
CTRL C 终止程序。

SIGINT 陷阱未注释:
SIGTERM 已发送:终止程序。
?来自另一个终端窗口的 SIGINT 被忽略!
CTRL C 执行陷阱代码并终止程序。

之前的行为?是意外的或错误的。我不太关心 SIGINT 行为。我尝试了这些,看看是否有陷阱有效。

所以,我删除了贪睡功能,只有:

sleep 60m &
wait $!

现在,kill -TERM -pid终止两个进程并执行 SIGTERM 陷阱。

在我的应用程序中,我正在等待系统关闭时的 SIGTERM,这样我就可以清理一些细节。我不知道系统关闭是否使用pid-pid,两者都对我有用,并且系统最终会杀死所有进程。我测试时的一些悬空睡眠进程没什么大不了的。

fifo 的解决方案相当复杂,我不明白。

谢谢您的帮助。

答案1

[在下面的注释中,请记住bash(或任何其他 shell)是一个 C 程序,即使脚本没有为其设置陷阱,它也可能为信号设置处理程序]

两个陷阱行均已注释:
SIGTERM已发送:终止程序。

由于bash没有设置任何处理程序SIGTERM,该信号将彻底终止它,导致启动它的 shell 返回提示符。但该sleep命令是外部程序并在单独的子进程中运行,将继续运行(pgrep -a sleep以验证)。

SIGINT从另一个终端窗口发送的消息被忽略!

When waiting for a child process (as sleep in this case), bash sets a signal handler for SIGINT, and if no trap was set for it, will ignore that signal, except for the case where the child process it's waiting for terminates because of the same signal. bash is able to gather from the exit status of the child whether it was killed by a signal, and which signal it was.

这就是所谓的“等待并配合退出”(世界教育理事会)且仅适用于SIGINTSIGQUIT信号。

其他 shell 可能不会这样做,并且可能只是延迟对SIGINT信号执行操作,直到它们等待的子进程终止为止。比较bash和 的示例dash

$ ( (sleep .2; pkill -INT -P $$)&); dash -c 'while :; do /bin/sleep 3; done'
<dash is killed by a self-raised SIGINT after 3 seconds>

$ ( (sleep .2; pkill -INT -P $$)&); bash -c 'while :; do /bin/sleep 3; done'
^C
<bash has to be killed by hand with Control-C>

CTRL C终止程序。

ACtrl-C会导致核心发送SIGINT信号至全部来自前台进程组(作业)的进程,在本例中包括两个都你的脚本及其sleep子脚本。您可以通过使用kill带有以下命令的命令来模拟否定的的pid流程组组长(与进程组 ID 相同),或者使用kill带有作业 ID 的内置 shell:

bash -c 'sleep 3600; echo DONE' &
[1] 12381
kill -INT -12381  # or kill -INT %1

使用脚本的否定 pid 仅当它确实是流程组组长。当从子 shell 或另一个脚本调用脚本或作为管道的一部分调用脚本时,情况可能并非如此。

陷阱SIGTERM未注释:
?SIGTERM已发送:程序未终止且SIGTERM陷阱未执行。

是的,陷阱将被执行。但仅限于sleep命令终止后。如果您不想等待 60 分钟来确认,请使用sleep 1代替;-)sleep 60m

链接的答案中也对此进行了解释,这是一个标准要求行为。

陷阱SIGINT未注释:
?SIGINT来自另一个终端窗口的信息被忽略!

同上。

答案2

我找到了一个简单的解决方案。

function snooze {
    sleep $1
}

while true
do
    snooze 60m &
    wait $!
done

这将执行陷阱,然后在收到 SIGTERM 后立即终止。

感谢你的协助。

相关内容