【问题标题】:How to use trap reliably using Bash running foreground child processes如何使用运行前台子进程的 Bash 可靠地使用陷阱
【发布时间】:2016-01-19 07:45:35
【问题描述】:

我有一个在前台运行一个长时间运行的进程的 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”只是一个代表进程。该进程实际上可以是一个交互式子外壳(例如,bash -i)。

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

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

此脚本旨在在包含 Bash 的任何最新版本的 Linux 发行版上运行(例如,不是 Cygwin)。

参考资料:

  1. killing Parent process along with child process using SIGKILL
  2. Kill bash and child process

【问题讨论】:

    标签: bash


    【解决方案1】:

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

    【讨论】:

    • 我必须将此标记为答案,因为您提出了我的问题,这很有帮助。然而,我说“那个进程实际上可以是一个交互式子shell(例如,bash -i)。”我应该添加“我不想把代表进程放在后台,因为它可能是一个交互式shell”。
    • 请记住,交互式子shell的用户无论如何都可以设置自己的陷阱,以避免shell被调用者打断。
    • @bgoodr 你有没有找到不需要将进程置于后台的解决方案?
    • @trex005 不,很遗憾我没有。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      您可以将长 sleep 命令替换为短 sleep 命令循环:

      while true
      do
          sleep 1
      done
      

      这样,如果你向 bash 进程发送信号,它会在当前正在执行的sleep 命令结束后立即做出反应,也就是在发送后最多 1 秒。

      【讨论】:

      • 谢谢,但请参阅我在stackoverflow.com/questions/33240221/… 对@chepner 的回答的回复。如果我只想睡觉,那么你的回答就足够了,但这只是一个代表性的进程,我最好允许它在前台运行,因为它最终可能是一个交互式的 bash -i 进程。跨度>
      猜你喜欢
      • 2023-03-13
      • 2012-03-21
      • 1970-01-01
      • 2018-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-17
      相关资源
      最近更新 更多