スクリプトのバックグラウンドプロセスが端末のSIGINT信号を受け取るかどうかを判断するにはどうすればよいですか?

スクリプトのバックグラウンドプロセスが端末のSIGINT信号を受け取るかどうかを判断するにはどうすればよいですか?
#!/usr/bin/env bash

sleep 3 && echo '123' &
sleep 3 && echo '456' &
sleep 3 && echo '999' &

これを実行してSIGINTcontrol-cを押して端末を介して送信すると、まだ出力がエコーされているようです123...。どういうわけか分離されたからだと思いますか?

wait < <(jobs -p)ただし、スクリプトの最後に追加(すべてのバックグラウンドジョブが完了するまで待機)してから実行して送信すると、SIGINT出力123...は次のようになります。いいえ見せる

この行動をどのように説明すべきでしょうか?wait信号を傍受してバックグラウンドプロセスに転送する方法はありますか?それとも、プロセスが端末に「接続」されているかどうかに関する状態ですか?

これに関連する可能性がある質問を見つけましたが、これが上記の動作とどのように関連しているかはわかりません。wait() 関数を使用するときに SIGCHLD 信号が無視されないのはなぜですか?

答え1

信号遮断?拡散?いいえ

wait信号を傍受しません。シェルはそれを通過できません。信号は傍受または伝播されません。 3つの「バックグラウンド」コマンドで構成されたサブシェルを実行するか、シグナルを受け取ります。まっすぐまたは。

次のスクリプトを使用してテストできます。

#!/usr/bin/env bash

printf 'My PID is %s.\n' "$$"
sleep 15 && echo '123' &
sleep 15 && echo '456' &
sleep 15 && echo '999' &
wait < <(jobs -p)

スクリプトは次のように動作しますwait。終了できるという意味です。そしてCtrl+を使用する3つの「バックグラウンド」タスクC

Ctrl+をクリックせずにやり直してくださいC。スクリプトは独自のPID($$)を通知します。SIGINT別の端末(kill -s INT <pid_here>)から送信するとスクリプトは終了しますが、いいえ3つの職業。

SIGINTただし、プロセスグループ全体()に送信すると、kill -s INT -- -<pid_here>スクリプトはそして物事はそれを得るでしょう。Ctrl+を押して端末がキー入力(通常はinclude)Cに対して割り込み信号を送信するように設定されている場合でも、同じことが発生します。stty -aintr = ^Cグループ全体信号を受け取ります。


誰が信号を受け取りますか?

前景プロセスグループについてです。シェルが実行するタスクの1つは、どのプロセスグループがフォアグラウンドにあるかを端末に通知することです。

端末には、それに関連する前景プロセスグループがあります。このフォアグラウンドプロセスグループは、信号を処理して入力文字[...]を生成するのに特別な役割を果たします。

タスク制御をサポートするコマンドソルバープロセスは、関連プロセスを単一のプロセスグループに配置し、そのプロセスグループを端末に関連付けることによって、端末を他のタスクまたはプロセスグループに割り当てることができます。 [… ]

源泉)

あなたの場合、実際に起こっていることは次のとおりです。

  1. 最初はインタラクティブシェルにあります。シェルは独自のプロセスグループのリーダーです(プロセスグループIDはシェルのPIDと同じです)。端末はこのプロセスグループを前景プロセスグループとして認識します。
  2. 上記のシェルでは、ジョブ制御が有効になっています。 (通常、ジョブ制御をサポートするシェルは対話的に実行されるとデフォルトで有効になります。)スクリプト(またはデフォルトでは組み込まれていないスクリプト)を起動すると、シェルはそのスクリプトを別のプロセスグループのリーダーにします。端末には、新しいプロセスグループを前景プロセスグループと見なす必要があるという通知が送信されます。インタラクティブシェルはこの方法でバックグラウンドに配置されます。
  3. shebangでは、我々はそれがbashシナリオを解釈していることを知っています。非対話型Bashは、デフォルトでジョブ制御が無効になっている状態で開始されます。実行するコマンドいいえ独自のプロセスグループのリーダーになります(コマンド自体がプロセスグループを変更しない限り、これは可能ですが、あなたの場合は発生しません)。これら3つのサブシェルと3つのsleepプロセスは、実行中のスクリプトと同じプロセスグループに属します。
  4. スクリプトが終了すると、対話型シェルのプロセスグループが(再び)前景プロセスグループと見なされる必要があるという通知が端末に渡されます。スクリプトプロセスグループのプロセスがまだ実行されている場合、そのプロセスはフォアグラウンドプロセスグループに属しなくなりました。

これは観察された動作を説明します。

  • Ctrl+を押したときにスクリプトが実行されている場合、スクリプトCとデフォルトですべての子がそれを受け取りますSIGINT。スクリプトはwait何も起こらないので待ちます&。重要なのは、そのプロセスグループがまだフォアグラウンドプロセスグループであり(孫)です。 )子プロセスはこのグループに属します。

  • Ctrl「+」をクリックしたときにスクリプトが実行されなくなった場合、その子は存在しないため、CそのSIGINTスクリプトを受け取ることができません。現在のフォアグラウンドプロセスグループ。


ジョブコントロールで遊ぶ

ジョブ制御を無効または有効にすることで、この動作を変更できます。

ジョブ制御()を無効にして対話型シェルでスクリプトを実行すると、シェルはスクリプトを実行しますが、set +mそのスクリプトをプロセスグループのリーダーにすることはありません。スクリプトにはジョブ制御はありません。このスクリプトとすべての添字は、デフォルトで対話型シェルのプロセスグループに属します。このグループは常にフォアグラウンドプロセスグループになります。Ctrl+以降、スクリプトがまだ実行されているかどうかに関係なく、Cすべてのプロセス(対話型シェルを含む)がそれを受け取ります。SIGINT

set -mスクリプト自体でジョブ制御()を有効にすると、3つのサブシェルが独自のプロセスグループに配置されます。同様の状況では、存在しないコマンドは&プロセスグループリーダーになり、端末は新しいフォアグラウンドプロセスグループに関する情報を受け取ります。しかし、あなたの命令は、彼らがリーダーになるが、&前景プロセスグループは変わらないということです。Ctrl+、スクリプトがまだ実行されているか、対話型シェルでジョブ制御が有効になっているかにかかわらず、Cそれを受け取りません。SIGINT


ノート

  • 区切り文字または終了記号の意味は、しばしば「&バックグラウンドで実行中」と記述されますが、「フォアグラウンドプロセスグループでは実行されません」とは異なります。実行中のコマンドは、&フォアグラウンドプロセスグループに残ることができます(たとえば、ジョブ制御が無効になっている場合など)。実行されないコマンドは、&フォアグラウンドプロセスグループを離れることができます。確かなのは、&「非同期的に実行」されることです。

  • インタラクティブシェルの配置が可能であるという事実に驚くこともあります。それ自体コマンドを実行すると、バックグラウンドで実行されます。これが起こります。対話型 Bash で次のコマンドを実行します。

    set -m
    trap 'echo "Signal received."' INT
    sleep 999
    

    Ctrl+C

    sleep中断されたことがわかりますが、シェルは信号を受信しません。これは、キーストロークが行われると、シェルが前景sleepプロセスグループである別のプロセスグループに配置されるためです。シェルは(then)フォアグラウンドプロセスグループにはありません。これはバックグラウンドにあることを意味します。

    次にset -m、次に変更しset +mてもう一度実行してください。

    set +m
    trap 'echo "Signal received."' INT
    sleep 999
    

    Ctrl+C

    ジョブ制御を無効にすると、sleepシェルのプロセスグループで実行されます。このグループは常にフォアグラウンドプロセスグループになります。から送信されたメッセージが表示されますtrap

関連情報