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가 종료되면 프로세스를 종료하는 방법은 무엇입니까?

답변1

그리고 zsh:

pids=()
trap '
  trap - CHLD
  (($#pids)) && kill $pids 2> /dev/null
' CHLD

sleep 2 & pids+=$!
sleep 1 & pids+=$!
sleep 3 & pids+=$!

wait

(여기서는 sleep테스트 명령으로 사용됨)

bashCHLD 트랩은 이 옵션이 켜져 있을 때만 실행되는 것으로 보입니다 . 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측면에서 거의 올바른 작업을 수행합니다 . 보시 다시피, 신호 핸들러를 설정할 때 인터럽트가 가능해야 합니다.waityashdashmkshwaitwait(),ㅏsleep(), 또는read()가지고 다닐 수 있는sleep()(물론 이전 호출에서 인터럽트가 발생한 경우 이상하게 동작할 수 있지만alarm()). 어느(차단되지 않음/무시됨)신호는 wait().

내 생각에는 이러한 것들의 쉘 구현은 그다지 다르지 않아야 하지만... 일부는 다릅니다. 특히 bash,,,,,의 최악의 모습을 보여줍니다. 그리고bashksh93dashmkshyashzshzshksh93 거의다음 시퀀스는 올바르게 실행되지만 첫 번째 종료 프로세스의 종료 상태를 유지하지 못합니다. 끔찍한 것은 아닙니다. 가장 최근에 종료된 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

bash3초 이내에 종료되지 않는 유일한 쉘입니다. zsh그리고 ksh93둘 다(제 생각에는 이것은 잘못된 것 같습니다) exit 0, 그렇지 않은 경우에는 3초 이내에 종료하시기 바랍니다. 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

관련 정보