从 bash 中,我生成了两个进程。这两个过程相互依赖。如果其中一人死亡,我希望两人都退出。最干净的方法是什么?目前我有以下内容:
# start process a
/bin/program_a;
a_pid=$!
# start process b
/bin/program_b;
b_pid=$!
# kill process b if process a exits
wait $a_pid
echo "a_pid died, killing process b"
kill -9 $b_pid
但这只会在进程 a 死亡时帮助进程 b 退出。如果进程 b 死亡,如何让进程 a 退出?
答案1
和zsh
:
pids=()
trap '
trap - CHLD
(($#pids)) && kill $pids 2> /dev/null
' CHLD
sleep 2 & pids+=$!
sleep 1 & pids+=$!
sleep 3 & pids+=$!
wait
(此处用作sleep
测试命令)。
这样看来,CHLD 陷阱仅在该选项打开bash
时运行。m
您不想在该选项下启动作业,因为这会在单独的进程组中运行它们。另请注意,在处理程序中重置处理程序似乎不适用于 bash。所以bash
等价的就是这样的:
pids=()
gotsigchld=false
trap '
if ! "$gotsigchld"; then
gotsigchld=true
((${#pids[@]})) && kill "${pids[@]}" 2> /dev/null
fi
' CHLD
sleep 2 & pids+=("$!")
sleep 1 & pids+=("$!")
sleep 3 & pids+=("$!")
set -m
wait
set +m
答案2
在我测试的那些 shell 中,据我所知,三个 shell 在SIGCHLD
和方面几乎做了正确的事情wait
:yash
、dash
和mksh
。你看,wait
应该是可中断的;当设置信号处理程序时,您需要该处理程序要么执行wait()
, Asleep()
,或一个read()
便携式的sleep()
(尽管如果中断来自先前的调用,显然可能会表现得很奇怪alarm()
)。任何(未被阻止/忽略)信号应停止 a wait()
。
在我看来,这些东西的 shell 实现不应该有太大的不同,但是......有些确实如此。特别是表现出、、、、或bash
中最差的。和bash
ksh93
dash
mksh
yash
zsh
zsh
ksh93
几乎正确执行以下序列,但它们无法保留第一个退出进程的退出状态。这并不可怕——尽管zsh
它也抱怨被要求使用 wait
最近退出的 pid。
这就是我所做的:
unset IFS
script=$(cat <<""
PS4="$0 + "
trap ' for p ### loop over bgd pids
do shift ### clear current pid
if kill -0 "$p" 2>/dev/null ### still running?
then set -- "$@" "$p" ### then append again
else wait "$p" ### else get return
exit "$(kill "$@")$?" ### kill others; exit
fi
done' CHLD ### wait til CHLD
for n in $(shuf -i 3-7) ### randomize order
do (sleep "$n";exit "$n")& set "$@" "$!" ### sleep 3 exits 3
done; set -x; wait ### debug, wait
)
上面的代码不仅应该在 shell 返回时立即杀死所有剩余的后台子 shell,而且还应该将第一个返回的子 shell 的退出代码传播到父 shell 的退出代码。它应该工作,因为wait
如果调用尚未等待的子进程,则应立即返回后台进程的退出状态。因为 SIGCHLD 是终止第一个的信号,所以wait
第二个wait
应该标记第一的第一个返回的孩子实际等待的时间。至少,简单地说应该是这样。然而,shell 实现越复杂,这种逻辑似乎就越不可靠。
这就是$script
我这样做时每个炮弹运行的情况......
for sh in yash zsh ksh bash mksh dash
do time "$sh" +m -c "$script" ### no job control
done
bash
是唯一一个在三秒内不会退出的 shell。zsh
以及ksh93
两者(在我看来,这是错误的) exit 0
,但否则请在三秒内退出。其他人exit 3
3秒之内。以下是测试结果:
yash + wait
yash + shift
yash + wait 19111
yash + kill 19112 19113 19116 19117
yash + exit 3
real 0m3.013s
user 0m0.007s
sys 0m0.000s
zsh + wait
zsh + p=19124
zsh + shift
zsh + kill -0 19124
zsh + set -- 19125 19127 19129 19132 19124
zsh + p=19125
zsh + shift
zsh + kill -0 19125
zsh + wait 19125
zsh:wait:12: pid 19125 is not a child of this shell
zsh + kill 19127 19129 19132 19124
zsh + exit 0
real 0m3.023s
user 0m0.017s
sys 0m0.000s
ksh + wait
ksh + shift
ksh + kill -0 19137
ksh + 2> /dev/null
ksh + set -- 19138 19139 19140 19141 19137
ksh + shift
ksh + kill -0 19138
ksh + 2> /dev/null
ksh + wait 19138
ksh + kill 19139 19140 19141 19137
ksh + exit 0
real 0m3.018s
user 0m0.000s
sys 0m0.010s
bash + wait
real 0m7.018s
user 0m0.007s
sys 0m0.007s
mksh + wait
mksh + shift
mksh + 2>/dev/null
mksh + kill -0 19157
mksh + set -- 19158 19159 19160 19161 19157
mksh + shift
mksh + 2>/dev/null
mksh + kill -0 19158
mksh + set -- 19159 19160 19161 19157 19158
mksh + shift
mksh + 2>/dev/null
mksh + kill -0 19159
mksh + set -- 19160 19161 19157 19158 19159
mksh + shift
mksh + 2>/dev/null
mksh + kill -0 19160
mksh + set -- 19161 19157 19158 19159 19160
mksh + shift
mksh + 2>/dev/null
mksh + kill -0 19161
mksh + wait 19161
mksh + kill 19157 19158 19159 19160
mksh + exit 3
real 0m3.022s
user 0m0.003s
sys 0m0.000s
dash + wait
dash + shift
dash + kill -0 19165
dash + set -- 19166 19168 19170 19173 19165
dash + shift
dash + kill -0 19166
dash + wait 19166
dash + kill 19168 19170 19173 19165
dash + exit 3
real 0m3.008s
user 0m0.000s
sys 0m0.000s