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가 종료되면 프로세스를 종료하는 방법은 무엇입니까?
답변1
그리고 zsh
:
pids=()
trap '
trap - CHLD
(($#pids)) && kill $pids 2> /dev/null
' CHLD
sleep 2 & pids+=$!
sleep 1 & pids+=$!
sleep 3 & pids+=$!
wait
(여기서는 sleep
테스트 명령으로 사용됨)
bash
CHLD 트랩은 이 옵션이 켜져 있을 때만 실행되는 것으로 보입니다 . 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
제가 테스트한 쉘 중에서 제가 알 수 있는 한 세 개의 쉘은 및 : , 및 SIGCHLD
측면에서 거의 올바른 작업을 수행합니다 . 보시 다시피, 신호 핸들러를 설정할 때 인터럽트가 가능해야 합니다.wait
yash
dash
mksh
wait
wait()
,ㅏsleep()
, 또는read()
가지고 다닐 수 있는sleep()
(물론 이전 호출에서 인터럽트가 발생한 경우 이상하게 동작할 수 있지만alarm()
). 어느(차단되지 않음/무시됨)신호는 wait()
.
내 생각에는 이러한 것들의 쉘 구현은 그다지 다르지 않아야 하지만... 일부는 다릅니다. 특히 bash
,,,,,의 최악의 모습을 보여줍니다. 그리고bash
ksh93
dash
mksh
yash
zsh
zsh
ksh93
거의다음 시퀀스는 올바르게 실행되지만 첫 번째 종료 프로세스의 종료 상태를 유지하지 못합니다. 끔찍한 것은 아닙니다. 가장 최근에 종료된 PID를 zsh
사용하라는 요청을 받는 것에 대해 불평하기도 합니다 .wait
이것이 내가 한 일입니다:
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
)
위 코드는 셸이 반환되자마자 남아 있는 모든 백그라운드 하위 셸을 종료해야 할 뿐만 아니라 첫 번째 반환된 하위 셸의 종료 코드를 상위 셸의 종료 코드로 전파해야 합니다. 그것~해야 한다wait
아직 대기하지 않은 하위 프로세스를 호출하면 백그라운드 프로세스의 종료 상태가 즉시 반환되어야 하기 때문에 작동합니다 . SIGCHLD는 첫 번째 신호를 종료하는 신호이므로 wait
두 번째 신호를 wait
표시해야 합니다.첫 번째처음 반환된 하위 항목이 기다려야 하는 실제 시간입니다. 적어도 간략하게는 그래야 합니다. 그러나 셸 구현이 복잡할수록 이 논리의 신뢰성은 떨어지는 것 같습니다.
$script
이 작업을 수행할 때 각 쉘이 실행되는 방식은 다음과 같습니다 .
for sh in yash zsh ksh bash mksh dash
do time "$sh" +m -c "$script" ### no job control
done
bash
3초 이내에 종료되지 않는 유일한 쉘입니다. zsh
그리고 ksh93
둘 다(제 생각에는 이것은 잘못된 것 같습니다) exit 0
, 그렇지 않은 경우에는 3초 이내에 종료하시기 바랍니다. 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