首页 文章

如何使用Bash运行前台子进程可靠地使用陷阱

提问于
浏览
6

我有一个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$

请注意,"sleep 11666"只是一个代表性的过程 . 该过程实际上可以是交互式子shell(例如, bash -i ) .

为什么父 test_trap.sh 进程没有捕获SIGHUP信号?为什么只有在发出 sleep 11666 的过程时才会触发陷阱?

我不想使用无法捕获的SIGKILL,因为我需要在陷阱表达式中进行各种清理操作 .

此脚本适用于任何包含Bash的Linux发行版的最新版本(例如,不是Cygwin) .

参考文献:

3 回答

  • 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 是可选的 .

  • 0

    实际上,bash正在接收信号,但它处于不间断状态,等待 sleep 命令结束 . 当它结束时,bash会对信号作出反应并执行陷阱 .

    您可以使用短 sleep 命令循环替换long sleep 命令:

    while true
    do
        sleep 1
    done
    

    这样,如果您将信号发送到bash进程,它将在当前正在执行的 sleep 命令结束后立即响应,即在发送后最多1秒 .

  • 1

    尝试使用信号 SIGINT (与按Ctrl C发送的信号相同)而不是 SIGKILL . 其他信号仅在bash可以处理I / O或其他条件时起作用 .

相关问题