为什么我的陷阱没有触发?

为什么我的陷阱没有触发?

echo给定一个在接收到SIGSTOPor信号时的脚本SIGHUP

$cat test.sh 
function clean_up {
    echo "cleaning up!"
}

echo 'starting!'

trap clean_up SIGSTOP SIGHUP

sleep 100

我在后台运行它:

$./test.sh > output &
[4] 42624

然后,我用-1ie杀死了它叹息

$kill -1 42624

但它trap没有按我的预期工作,即该output文件没有cleaning up!内容。

$cat output 
starting!

发生了什么?

答案1

手册页bash状态

如果 bash 正在等待命令完成并接收到已设置陷阱的信号,则在命令完成之前不会执行陷阱。

在您的示例中,在完成trap之前不会执行。sleep 100

答案2

实际上它确实打印了,但你必须等待 100 秒才能看到结果。

你说:

$./test.sh > output &
[4] 42624

如果你检查ps aux,你会得到类似的内容:

xiaobai  42624  0.0  0.1 118788  5492 pts/3    S    04:07   0:00 /bin/bash
xiaobai  42626  0.0  0.0 108192   668 pts/3    S    04:07   0:00 sleep 100

你的主脚本进程/bin/bash,PID 42624仍在等待sleep 100完成。即使主进程收到信号1,它也必须sleep 100先等待完成,然后才轮到它执行任务,即echo "cleaning up!"

您可以使睡眠进程成为后台进程。在这种情况下,当且仅当脚本进程尚未退出时,脚本进程可以执行而echo "cleaning up!"无需等待。sleep process

我们可以通过这个脚本证明概念(即脚本sleep在处理信号之前等待):

function clean_up {
    echo "cleaning up!"
}

echo 'starting!'

trap clean_up SIGHUP
sleep 5000 &
echo 1
sleep 20
echo 2
sleep 10
echo 3

像往常一样运行这个脚本./test.sh > output,然后使用ps aux来找出/bin/bashPID 是 23311,然后执行kill -1 23311.同时cat output要知道这个阶段,即当脚本进入sleep 20echo 1 之后,发送kill -1 23311,现在等待 2 出来,你会发现“cleaning up”已经在 2 之前一起打印了。最后轮到echo 3脚本了出口。

$ cat output                                                                                            
starting!
1
cleaning up!
2
3
$ 

上面的实验证明,脚本接收 SIGHUP 信号并在所有前面的前台进程完成后执行信号处理程序,而无需等待后台进程。

故事变得有趣了,有了你原来的剧本,如果你这么做了怎么办kill -1 PID_of_sleep_100

它将退出所有进程而不打印,因为睡眠进程不会向脚本进程返回 SIGHUP。

因此,您的任务有一个解决方案,您可以执行kill -1 PPID(脚本进程)来确认 SIGHUP 到脚本进程,然后kill -1 PID(睡眠进程)。现在脚本进程将在退出之前执行 SIGHUP 处理程序,快乐黑客:)

并非所有信号都相同,例如SIGKILL 不允许陷阱。如果您杀死 -9 脚本进程,则睡眠进程仍将运行,并且其父 PID 将变为 1(通过检查ps -ef)。

[更新]:

通常情况下kill -N script_PID下会如果信号 N 没有捕获到您的脚本中,则直接终止脚本。但要小心 SIGINT,kill -2 script_PID即使你的脚本没有捕获它,它也不会直接杀死它。您的脚本将等待您的子进程(例如 sleep 10)完成。现在假设您在 script_PID 中执行多次终止,即kill -2 script_PIDAND kill -user-defined_trap_N script_PID,然后:

  1. 如果睡觉等待10秒后正常返回或者通过 2 以外的信号杀死,您的脚本在返回时将忽略缓存的信号2,然后执行用户定义的_trap_N函数。
  2. 但如果睡觉信号2杀死,那么你的脚本将在返回时执行内置的SIGINT处理程序,然后直接kill而不执行user-defined_trap_N。

相关内容