从 bash 生成两个进程,如果其中一个进程退出,则退出两个进程

从 bash 生成两个进程,如果其中一个进程退出,则退出两个进程

从 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和方面几乎做了正确的事情waityashdashmksh。你看,wait应该是可中断的;当设置信号处理程序时,您需要该处理程序要么执行wait(), Asleep(),或一个read()便携式的sleep()(尽管如果中断来自先前的调用,显然可能会表现得很奇怪alarm()。任何(未被阻止/忽略)信号应停止 a wait()

在我看来,这些东西的 shell 实现不应该有太大的不同,但是......有些确实如此。特别是表现出、、、、或bash中最差的。和bashksh93dashmkshyashzshzshksh93 几乎正确执行以下序列,但它们无法保留第一个退出进程的退出状态。这并不可怕——尽管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 33秒之内。以下是测试结果:

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

相关内容