web-dev-qa-db-ja.com

親プロセスは、SIGINTを受信した後、デフォルトのアクションを実行しません

SIGINTについて2つのテストを行いました Ctrl+C bashで。

テスト1

親スクリプトと子スクリプトの両方がtrapによってハンドラーを設定します。

# !/bin/bash
# Parent.sh
trap 'echo "I handled the SIGINT in Parent.sh and expect the script going ahead."' SIGINT
echo 1.1
./Child.sh
echo 1.2
# !/bin/bash
# Child.sh
trap 'echo "I handled the SIGINT in Child.sh and expect the script going ahead."' SIGINT
echo 2.1
sleep 1000000000
echo 2.2
/tmp:$ ./Parent.sh
1.1
2.1
^CI handled the SIGINT in Child.sh and expect the script going ahead.
2.2
I handled the SIGINT in Parent.sh and expect the script going ahead.
1.2

出力は私の期待とかなり一致しています。

テスト2

子スクリプトのみがtrapによってハンドラーを設定します。

# !/bin/bash
# Parent.sh
# trap 'echo "I expect I will be terminated immediately after Child.sh died."' SIGINT
echo 1.1
./Child.sh
echo "sleep started"
sleep 5
echo "sleep finished"
echo 1.2
# !/bin/bash
# Child.sh
trap 'echo "I handled the SIGINT in Child.sh and expect the script going ahead."' SIGINT
echo 2.1
sleep 1000000000
echo 2.2
/tmp:$ ./Parent.sh
1.1
2.1
^CI handled the SIGINT in Child.sh and expect the script going ahead.
2.2
sleep started
sleep finished
1.2

Parent.shが死んだ後、Child.shは(SIGINTに関するデフォルトのアクションとして)すぐに終了するはずだと思っていました。

しかし、Parent.shはSIGINTを受け取ったようですが、それを無視しました。

なぜこれなのか?

2
Anon

これは、bashが実装する "Wait and Cooperative Exit" (WCE)が原因で発生します。

Bashが子を待っている間にSIGINTシグナルを受信すると、子が終了した場合ではなく、そのシグナルが原因で子が死亡した場合にのみbashが自殺します。親プロセスは、子の終了ステータスから、処理されていないシグナルが原因で終了したか終了したかを判断できます。 wait(2) マンページのWIF*マクロを参照してください。

より簡単な例:

bash -c 'sh -c "trap exit INT; read p"; echo DONE'
<ctrl-C>
DONE

そして、dashシェルを使用すると、WCEは実装されません。

dash -c 'sh -c "trap exit INT; read p"; echo DONE'
<ctrl-C>
<no DONE, parent script was killed outright>

同様の Q&A 、および kludge 回避策として提案したものを参照してください。

3
mosvy