如何解决为什么等待完成的父 shell 进程无法可靠地接收从 Bash 脚本中的后台作业发送的 USR1 信号?
我有一个 Bash 脚本并行运行一堆后台作业。 在某些条件下,在后台作业完成之前,它会发送 向生成的 Bash 进程发送 USR1 信号(例如,通知 作为作业的一部分运行的某些进程已终止 非零退出代码)。
在简化形式中,该脚本等效于如下所示的脚本。
在这里,为了简单起见,每个后台作业总是发送一个 USR1 信号
在完成之前,无条件地(通过 signalparent()
函数)。
signalparent() { kill -USR1 $$; }
handlesignal() { echo 'USR1 signal caught' >&2; }
trap handlesignal USR1
for i in {1..10}; do
{
sleep 1
echo "job $i finished" >&2
signalparent
} &
done
wait
当我运行上述脚本时(至少在 macOS 11.1 上使用 Bash 3.2.57), 我观察到一些我无法解释的行为,这让我思考 Bash 作业管理和 我忽略的信号捕获。
具体来说,我想获得以下解释 行为。
-
几乎总是,当我运行脚本时,我看到更少的“信号捕获” 输出中的行(来自
handlesignal()
函数)比那里 是在for
循环中启动的作业——大部分时间是 为正在启动的 10 个作业打印的其中 4 行。 -
同时,每隔一段时间,在一些脚本调用中, 我观察到
kill
命令(来自signalparent()
函数) 报告有关运行脚本的原始进程的错误 (即,带有$$
PID 的那个)不再存在——参见 输出如下。为什么有些作业的信号
kill
命令仍然存在 在父 shell 进程已经终止时运行? 我的理解是父母不可能 进程在所有后台作业之前终止,因为wait
调用。job 2 finished job 3 finished job 5 finished job 4 finished job 1 finished job 6 finished USR1 signal caught USR1 signal caught job 10 finished job 7 finished job 8 finished job 9 finished bash: line 3: kill: (19207) - No such process bash: line 3: kill: (19207) - No such process bash: line 3: kill: (19207) - No such process bash: line 3: kill: (19207) - No such process
这两种行为都向我表明存在竞争条件 某种,我不太明白它的起源。我会 不胜感激,如果有人能在这些方面启发我,甚至可能 建议如何更改脚本以避免此类竞争条件。
解决方法
这在 Bash Reference Manual 中解释如下。
当 bash 通过 wait
内置函数等待异步命令时,接收到设置了陷阱的信号将导致 wait
内置函数立即返回,退出状态大于128,紧接着执行陷阱。
因此,您需要重复 wait
直到它返回 0 以确保所有后台作业都已终止,例如:
until wait; do
:
done
据我所知,由于 wait
调用,父进程不可能在所有后台作业完成之前终止。
那是个误会; wait
可能会在后台运行作业时接收到设置了陷阱的信号而返回,这可能导致程序正常完成,副作用是使这些作业成为孤立的。
关于‘几乎总是,当我运行脚本时,我在输出中看到更少的“信号捕获”行’—
根据signal(7):
标准信号不排队。如果一个标准信号的多个实例在该信号被阻塞时生成,那么只有一个信号实例被标记为未决(并且该信号在解除阻塞时只会被传递一次)。
更改脚本以使信号不会同时到达的一种方法如下:
signalparent() {
kill -USR1 $$
}
ncaught=0
handlesignal() {
(( ++ncaught ))
echo "USR1 signal caught (#=$ncaught)" >&2
}
trap handlesignal USR1
for i in {1..10}; do
{
sleep $i
signalparent
} &
done
nwaited=0
while (( nwaited < 10 )); do
wait && (( ++nwaited ))
done
这是在 macOS 10.15 上使用 Bash 5.1 修改后的脚本的输出:
USR1 signal caught (#=1)
USR1 signal caught (#=2)
USR1 signal caught (#=3)
USR1 signal caught (#=4)
USR1 signal caught (#=5)
USR1 signal caught (#=6)
USR1 signal caught (#=7)
USR1 signal caught (#=8)
USR1 signal caught (#=9)
USR1 signal caught (#=10)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。