捕获信号时如何正确等待bash子进程完成

时间:2019-04-10 17:39:05

标签: linux bash docker delayed-job

我们有一个包装器脚本,可在后台启动DelayedJob worker。该脚本等待,直到DelayedJob工作程序完成后再退出。包装脚本是Docker容器的主要入口点,它设置了DJ工作者运行所需的一些环境。

我们注意到,尽管发出Docker停止命令时,Docker容器应等待DJ工作者正常退出(或直到最大超时),但这种情况不会发生。容器立即退出。

向容器发出Docker stop调用会将SIGTERM发送给主进程包装器脚本。在包装脚本中,我们捕获了SIGTERM并将信号传递给DJ工作进程。

这仍然行不通。我使用简单的Bash脚本创建了一个测试案例,用以说明问题。

脚本p1:

#!/bin/bash
echo "P1: starting p1 and running p2 in bg"
exit_script() {
  echo "P1: Caught sigterm in p1, sending TERM to p2"
  kill -TERM $child
}

trap exit_script SIGINT SIGTERM

./p2 &
child=$!

echo "P1: waiting for p2 ($child)"
wait $child

echo "P1: Finished waiting for p2, exiting p1"

脚本p2:

#!/bin/bash
echo "P2: starting p2"
exit_script() {
  echo "P2: Caught sigterm"
  NEXT_WAIT_TIME=0
  until [ $NEXT_WAIT_TIME -eq 10 ]; do
    echo "P2: EXIT_SCRIPT loop $NEXT_WAIT_TIME"
    sleep $(( NEXT_WAIT_TIME++ ))
  done  
  exit
}

trap exit_script SIGINT SIGTERM

echo "P2: Sleeping for a while"

NEXT_WAIT_TIME=0
until [ $NEXT_WAIT_TIME -eq 10 ]; do
  echo "P2: Main Loop $NEXT_WAIT_TIME"
  sleep $(( NEXT_WAIT_TIME++ ))
done

echo "P2: Finished sleeping in p2"

输出:

MBP:$ ./p1
P1: starting p1 and running p2 in bg
P1: waiting for p2 (74039)
P2: starting p2
P2: Sleeping for a while
P2: Main Loop 0
P2: Main Loop 1
P2: Main Loop 2
P2: Main Loop 3
P2: Main Loop 4
P1: Caught sigterm in p1, sending TERM to p2
P1: Finished waiting for p2, exiting p1
MBP:$ P2: Caught sigterm
P2: EXIT_SCRIPT loop 0
P2: EXIT_SCRIPT loop 1
P2: EXIT_SCRIPT loop 2
P2: EXIT_SCRIPT loop 3
P2: EXIT_SCRIPT loop 4
P2: EXIT_SCRIPT loop 5
P2: EXIT_SCRIPT loop 6
P2: EXIT_SCRIPT loop 7
P2: EXIT_SCRIPT loop 8
P2: EXIT_SCRIPT loop 9

如您所见,在捕获信号时调用的wait函数中的代码之前,将执行对exit_script的p1脚本调用之后的行。

一种解决方案是将wait替换为检查子PID是否存在的超时循环,但是为什么wait不能按预期工作? wait的用法不正确吗?

1 个答案:

答案 0 :(得分:2)

等待被传入的信号中断,并且没有重新开始。您应该能够仅添加另一个等待调用以强制其完成等待。不过,可能有更好的方法。

echo "P1: waiting for p2 ($child)"
wait $child
wait $child

echo "P1: Finished waiting for p2, exiting p1"