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

Posted

技术标签:

【中文标题】捕获信号时如何正确等待bash子进程完成【英文标题】:How to properly wait for bash child process to complete when trapping signals 【发布时间】:2019-04-10 17:39:04 【问题描述】:

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

我们注意到,当发出 Docker stop 时,Docker 容器应该等到 DJ 工作者正常退出(或直到最大超时),但这并没有发生。容器立即退出。

向容器发出 Docker 停止调用会向主进程(包装脚本)发送 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

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

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

【问题讨论】:

【参考方案1】:

等待被传入信号中断,不会重新启动。您应该能够添加另一个等待调用来强制它完成等待。不过可能有更好的方法来做到这一点。

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

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

【讨论】:

以上是关于捕获信号时如何正确等待bash子进程完成的主要内容,如果未能解决你的问题,请参考以下文章

在 C 中等待子进程终止的最佳实践

shell向子进程发送信号

如何等待子进程发送信号?

Shell脚本入门 07:进程与信号

Shell脚本入门 07:进程与信号

如何通过子进程之间的共享内存共享信号量?