次の2つのスクリプトがあります。
script1.sh
:
#!/usr/bin/env bash
set -x
set -e
./script2.sh &
pid=$!
max_retry=15
counter=0
until curl --silent --head --fail localhost:8000; do
if ! jobs %1; then
echo "Server quit unexpectedly before reporting successful status"
exit 1
fi
if [[ $counter -eq $max_retry ]]; then
echo "Server never reported healthy status"
kill -INT $pid
wait $pid
exit 1
fi
echo "Server not ready. Sleeping for 1 second"
sleep 1
counter=$((counter + 1))
done
echo "Server responded healthy"
kill -INT $pid
wait $pid
echo "Done"
script2.sh
:
#!/usr/bin/env bash
set -x
set -e
docker run \
-p 8000:80 \
nginxdemos/hello
実行時にスクリプトがバックグラウンドで実行され、成功するまで実行してから終了するようにしたいと./script1.sh
思います。しかし、これは起こりませんでした。行26はスクリプトを終了しません。代わりに+を押すまで続けます。script2.sh
curl
script2.sh
SIGINT
kill
script2.sh
Ctrl
C
なぜこれが起こるのですか?サブプロセスに信号をscript2.sh
正しくシャットダウンして伝播する方法は?SIGINT
答え1
低レベルのオペレーティングシステムの観点から正しいアプローチは、PIDに負の値を渡してkill
プロセスグループID(PGID)を指定することです。
残念ながら、グループIDで終了しようとしているプロセスは、終了したい親プロセスと同じグループにあることがよくあります。ただし、親グループを含むグループ全体が終了します。
これはハッキングによってのみ解決することができる愚かなUnixの問題である。
私はあなたに似たscript1.sh
背景を持っています。私はコマンドがありません。私が殺したら、彼らはまだ生きています。script2.sh
docker
sleep 3600
script2.sh
script2.sh
sleep 3600
script2.sh
:
#!/bin/sh
sleep 3600
では、script1.sh
Awkプログラムを使用してプロセスのすべての子孫を見つけますscript2.sh
。
script1.sh
:
#!/bin/sh
./script2.sh &
pid=$!
sleep 1
descendants=$(awk -f descendants.awk $pid)
kill -TERM $pid
for desc in $descendants; do
kill -TERM $desc
done
# wait for script2.sh by pid
wait $pid
echo done
これは私のものですdescentants.awk
:
BEGIN {
# symbolic names for "ps -efj" columns
PID=2
PPID=3
root_process = ARGV[1]
while (("ps -efj" | getline) > 0)
parent[$PID] = $PPID
for (child in parent) {
pid = child;
do {
par = parent[pid]
if (par == root_process) {
print child
break
}
pid = par
} while (par != 0 && par != 1)
}
}
これは、出力の列2と3に基づいて関連ハッシュを構築することによって達成されますps -efj
。このハッシュは、システム内の特定のPIDの親エントリを提供します。その後、すべてのPIDを繰り返します。各pidの親族の鎖に沿って祖先を追跡します。指定されたpidを持つすべてのpidを直接または間接祖先として印刷します。
これは親が死亡し、PID 1にリセットされた子孫を見つけません(Awkスクリプトが血統データを収集する前)。
script2.sh
バックグラウンドに置いて終了する場合と同様に、sleep 3600 &
Awkスクリプトはsleep
引き続き実行されますが、PPIDは1です。
つまり、スクリプトとその子孫がうまく機能すると仮定します。何でも自分の思い通りに死んだらきれいに死んで子孫が残らないように気をつけてください。突然殺されると、私たちは将来の世代を担当します。
PS:私はGNU Linuxで働いています。出力をpstree -Tp
。 -T
それは私たちがしないもう一つのことです。私たちはスレッドについて話しません。プロセスを終了すると、そのスレッドがスレッドライブラリによって処理されると仮定します。スレッドは実際にLinuxのプロセスです。
例:これは出力の一部ですpstree -Tp
。
│ ├─firefox(22690)─┬─Isolated Web Co(22776)
│ │ ├─Isolated Web Co(22880)
│ │ ├─Privileged Cont(22870)
│ │ ├─Socket Process(22748)
│ │ ├─Web Content(22907)
│ │ ├─Web Content(22934)
│ │ ├─Web Content(27810)
│ │ └─WebExtensions(22820)
以下はスクリプトの出力です。
$ awk -f descendants.awk 22690
22820
22880
22907
22776
22870
22934
27810
22748
同じ8つのプロセスが見つかりました。サブツリーの深さが1を超える別の例:
│ ├─gnome-terminal-(6802)───bash(6814)─┬─git(365)───wish(368)─┬─aspell(382)
│ │ │ └─git-diff-index(15474)
│ │ ├─git(26636)───wish(26641)───aspell(26650)
│ │ ├─txr(15717)
│ │ └─vi(933)
$ awk -f descendants.awk 6802
365
26641
368
382
15474
26636
26650
933
15717
6814