SIGINT は子プロセスに伝播されません。

SIGINT は子プロセスに伝播されません。

次の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.shcurlscript2.shSIGINTkillscript2.shCtrlC

なぜこれが起こるのですか?サブプロセスに信号をscript2.sh正しくシャットダウンして伝播する方法は?SIGINT

ベストアンサー1

低レベルのオペレーティングシステムの観点から正しいアプローチは、PIDに負の値を渡してkillプロセスグループID(PGID)を指定することです。

残念ながら、グループIDで終了しようとしているプロセスは、終了したい親プロセスと同じグループにあることがよくあります。ただし、親グループを含むグループ全体が終了します。

これはハッキングによってのみ解決することができる愚かなUnixの問題である。

私はあなたに似たscript1.sh背景を持っています。私はコマンドがありません。私が殺したら、彼らはまだ生きています。script2.shdockersleep 3600script2.shscript2.shsleep 3600

script2.sh:

#!/bin/sh
sleep 3600

では、script1.shAwkプログラムを使用してプロセスのすべての子孫を見つけます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

おすすめ記事