私の質問は、WCE(Waiting and Cooperative Exit)とは何の関係もありません。インタラクティブシェルでフォアグラウンド操作でスクリプトを起動するとします。
#! /bin/bash
# script name: foreback.sh
sleep 100m & # child process in bg
sleep 200m & # ditto
sleep 300m & # ditto
sleep 7000m # child process in fg
exit 0
このスクリプトの実行中にCtrl-Cを押すと、すべてのフォアグラウンドプロセス(マイスクリプトのプロセス - 親プロセス - 予想される4番目のスリープサブプロセス)が終了します。
私の質問は:フォアグラウンドプロセスグループがSIGINTを受信したときにこれらのバックグラウンドサブプロセスをどのように識別しますか?
信号を送信する前に、次のps出力を確認してください。
TT TPGID PPID PID PGID SESS STAT COMMAND
pts/0 9373 9259 9282 9282 9282 Ss | \_ /bin/bash
pts/0 9373 9282 9373 9373 9282 S+ | | \_ /bin/bash ./foreback.sh
pts/0 9373 9373 9374 9373 9282 S+ | | \_ sleep 100m
pts/0 9373 9373 9375 9373 9282 S+ | | \_ sleep 200m
pts/0 9373 9373 9376 9373 9282 S+ | | \_ sleep 300m
pts/0 9373 9373 9377 9373 9282 S+ | | \_ sleep 7000m
親プロセスと子プロセスの両方は、親の前景プロセスのPIDである同じターミナルプロセスグループ(TPGID)に属し、STAT列にプラス記号が表示されるため、一種の「フォアグラウンドコンプレックス」として表示されます。 SIGINTをCtrl-C経由でフォアグラウンドプロセスグループに送信する場合、またはkill -INT経由でプロセスグループ(PGID)に送信する場合 -PGID、シェルは終了するプロセスとアクティブな状態を維持するプロセスをどのように知っていますか?上記のCtrl-Cまたはプロセスグループを使用して終了した後、ps出力は次のようになります。
TT TPGID PPID PID PGID SESS STAT COMMAND
pts/0 9282 9259 9282 9282 9282 Ss+ | \_ /bin/bash
pts/0 9282 1742 9374 9373 9282 S \_ sleep 100m
pts/0 9282 1742 9375 9373 9282 S \_ sleep 200m
pts/0 9282 1742 9376 9373 9282 S \_ sleep 300m
親プロセスのバックグラウンドで開始された 3 つのサブプロセスはまだアクティブであり、STAT 列はプラス記号が欠落してバックグラウンドにあることを示し、端末プロセスグループは対話型シェルのグループです。これがまさにその方法です。
しかし、SIGINTがフォアグラウンドプロセスグループに送信された場合、「私を殺さないでください。私はバックグラウンドプロセスです」という「フラグ」は表示されません。
私は次のことが起こっていると思います。
SIGINT はフォアグラウンドプロセスグループに送信されます。
信号を受信してそれに応答する最初のプロセスは、PIDがTPGID(フォアグラウンドプロセスグループリーダー)と同じプロセスです。
プロセスが終了すると、シェルはどのサブプロセスがバックグラウンドプロセスとして開始されたかを「記憶」し、そのTPGIDを元のTPGIDからインタラクティブシェルのTPGIDに変更し、以前の前景プロセスグループに属しなくなります。開始されず、TPGIDの変更を経験しなかった残りの子はSIGINTを受け取り、応答します(または他の処理が発生しない場合は終了)。
数週間、数多くのウェブサイトを閲覧しましたが、正解が見つかりませんでした。
どんなアイデアがありますか? ? ? ?ありがとう
答え1
デフォルトでは、非対話型シェルにはジョブ制御機能がないため、スクリプトが実行するすべてのプロセスはシェルを実行するプロセスと同じプロセスグループにあります。このプロセスグループは、スクリプトが起動されたのか/フォアグラウンドに配置されているのかに応じて、対話型シェル起動スクリプトのターミナルフォアグラウンドプロセスグループになります。
ただし、ジョブ制御を無効にすると、POSIXシェルはこの要件:
2.11。信号とエラー処理
シェルが非同期リストを実行しているときにジョブ制御が無効になっている場合(set -mの説明を参照)、リストのコマンドはシェルのSIGINTおよびSIGQUITシグナルの無視(SIG_IGN)シグナル操作を継承します。他のすべての場合、シェルによって実行されるコマンドは、トラップ特殊組み込み関数によってシグナル操作が変更されない限り、親からシェルによって継承されたのと同じシグナル操作を継承する必要があります(トラップを参照)。
したがって、これは非同期リストがCtrl+cとCtrl+の影響を受けないようなおおよその形の「作業」制御です\。
Linuxの場合:
$ sh -c 'sleep 10 & grep SigIgn "/proc/$!/status"'
SigIgn: 0000000000000006
$ kill -l INT QUIT
2
3
(SigIgn
上記はビットマスクです。この場合、SIGINTとSIGQUITに対応する2番目と3番目のビットが設定されます。)
これらのキーの組み合わせを押したときに端末がこれらの文字を送信すると、^C
シェル^\
はこれらのシグナリングに参加しません。カーネル(ttyドライバ)は、端末のフォアグラウンドプロセスグループ内のプロセスにシグナルを送信します。
/dev/pts/0
TPGIDは、実際のプロセスではなく、端末装置(あなたの場合)の属性です。ps
端末に接続されているセッションで実行されているすべてのプロセスに対して、常に同じ値が表示されます。タスク(プロセスグループ)が前景にインポートされると、対話型シェルは次の操作を実行してtcsetpgrp(terminal_fd, its_pid)
ターミナルドライバに次のことを知らせます。これはこの端末の前景プロセスグループであり、他はすべて背景にあります。
実際に生き残ったsleep
プロセスはスクリプトが終了するためバックグラウンドで実行されている^C
ので、これを待っている対話型シェルは端末のフォアグラウンドプロセスグループを再び独自のプロセスグループに変更します。