我有一个Bash脚本,它在前台运行一个长时间运行的进程。当它收到SIGQUIT信号时,它应该执行各种清理操作,例如查杀自身及其所有子进程(通过杀死进程组等)。应该捕获信号的最小脚本如下所示(称为test_trap.sh
):
#!/bin/bash
trap 'echo "TRAP CAUGHT"; exit 1' QUIT # other required signals are omitted for brevity
echo starting sleep
sleep 11666
echo ending sleep
echo done
我想将SIGHUP信号发送到test_trap.sh
脚本的进程。但是,向test_trap.sh
发送SIGHUP不会触发陷阱表达式,但只有当我将信号发送到子sleep 11666
进程时才会触发陷阱。下面是一个bash会话,展示了这一点:
bash-4.1$ test_trap.sh &
[1] 19633
bash-4.1$ starting sleep
bash-4.1$ kill -s SIGQUIT 19633
bash-4.1$ jobs
[1]+ Running test_trap.sh &
bash-4.1$ ps -ef --forest --cols=10000 | grep '11666\|test_trap.sh' | grep -v grep
theuser 19633 12227 0 07:40 pts/4 00:00:00 \_ /bin/bash ./test_trap.sh
theuser 19634 19633 0 07:40 pts/4 00:00:00 | \_ sleep 11666
bash-4.1$ kill -s SIGQUIT 19634
bash-4.1$ Quit (core dumped)
TRAP CAUGHT
[1]+ Exit 1 test_trap.sh
bash-4.1$ ps -ef --forest --cols=10000 | grep '11666\|test_trap.sh' | grep -v grep
bash-4.1$
注意"睡眠11666"只是一个代表性的过程。该过程实际上可以是交互式子shell(例如,bash -i
)。
为什么父test_trap.sh
进程没有捕获SIGHUP信号?为什么只有在sleep 11666
的过程发出信号时才会触发陷阱?
我不想使用无法捕获的SIGKILL,因为我需要在陷阱表达式中进行各种清理操作。
此脚本适用于任何包含Bash的Linux发行版的最新版本(例如,不是Cygwin)。
参考文献:
答案 0 :(得分:6)
bash
必须等待sleep
完成才能执行处理程序。一个好的解决方法是在后台运行sleep
,然后立即等待它。虽然sleep
不可中断,但wait
不是。
trap 'kill $sleep_pid; echo "TRAP CAUGHT"; exit 1' QUIT
echo starting sleep
sleep 11666 &
sleep_pid=$!
wait
echo ending sleep
echo done
记录sleep_pid
并使用它从处理程序中删除sleep
是可选的。
答案 1 :(得分:1)
实际上,bash正在接收信号,但它处于不可中断状态,等待sleep
命令结束。当它结束时,bash会对信号作出反应并执行陷阱。
您可以使用短sleep
个命令循环替换long sleep
命令:
while true
do
sleep 1
done
这样,如果您将信号发送到bash进程,它将在当前正在执行的sleep
命令结束后立即作出反应,即在发送后最多1秒。
答案 2 :(得分:0)
尝试使用信号SIGINT
(通过按 Ctrl + C 发送的信号,而不是SIGKILL
。其他信号仅在bash可以处理I / O或其他条件时才起作用。