web-dev-qa-db-ja.com

トラップがトリガーされなかったのはなぜですか?

echoまたはSIGSTOP信号を受信するとSIGHUPが実行されるスクリプトがあるとします。

$cat test.sh 
function clean_up {
    echo "cleaning up!"
}

echo 'starting!'

trap clean_up SIGSTOP SIGHUP

sleep 100

私はそれをバックグラウンドで実行しました:

$./test.sh > output &
[4] 42624

次に、-1、つまり [〜#〜] sighup [〜#〜] で殺しました

$kill -1 42624

しかし、trapは期待どおりに機能しませんでした。つまり、outputファイルの内容にcleaning up!が含まれていません。

$cat output 
starting!

どうした?

3
Kevin Meredith

実際には印刷されましたが、結果を表示するには100秒待つ必要があります。

あなたが言った:

_$./test.sh > output &
[4] 42624
_

_ps aux_をチェックすると、次のような結果が得られます。

_xiaobai  42624  0.0  0.1 118788  5492 pts/3    S    04:07   0:00 /bin/bash
xiaobai  42626  0.0  0.0 108192   668 pts/3    S    04:07   0:00 sleep 100
_

/ bin/bashのメインスクリプトプロセス、PID42624はまだスリープ100の終了を待っています。メインプロセスでもシグナル1を受信しましたが、最初に_sleep 100_が完了するのを待たなければなりません。その後、タスクを実行する番です(つまり、_echo "cleaning up!"_)。

スリーププロセスをバックグラウンドプロセスにすることができます。この場合、スクリプトプロセスは、スクリプトプロセスがまだ終了していない場合に限り、_echo "cleaning up!"_を待たずに_sleep process_を実行できます。

このスクリプトを使用して、概念(つまり、スクリプトがシグナルを処理する前にsleepを待機している)を証明できます。

_function clean_up {
    echo "cleaning up!"
}

echo 'starting!'

trap clean_up SIGHUP
sleep 5000 &
echo 1
sleep 20
echo 2
sleep 10
echo 3
_

このスクリプトを通常どおり_./test.sh > output_で実行し、次に_ps aux_を使用して_/bin/bash_ PIDが23311であることを確認し、次に_kill -1 23311_を実行します。同時に_cat output_がステージを知るために、つまり、スクリプトがエコー1の後に_sleep 20_に入ると、_kill -1 23311_を送信し、2が出るのを待ちます。 2の前に一緒に印刷されました。最後に、_echo 3_とスクリプトの終了です。

_$ cat output                                                                                            
starting!
1
cleaning up!
2
3
$ 
_

上記の実験により、スクリプトはシグナルSIGHUPを受信し、前のすべてのフォアグラウンドプロセスが完了した後、バックグラウンドプロセスを待たずに、シグナルハンドラーを実行することが証明されました。

元のスクリプトを使用して、ストーリーが面白くなります。_kill -1 PID_of_sleep_100_を実行するとどうなりますか?

スリーププロセスはSIGHUPをスクリプトプロセスに返さないため、印刷せずにすべてのプロセスを終了します。

したがって、タスクの解決策があります。_kill -1 PPID_(スクリプトプロセス)を実行して、SIGHUPをスクリプトプロセスに承認し、次に_kill -1 PID_(スリーププロセス)を実行できます。スクリプトプロセスは終了する前にSIGHUPハンドラーを実行します。ハッキングを楽しんでください:)

すべての信号が同じというわけではありません。 SIGKILLはトラップを許可していません 。スクリプトプロセスを-9で強制終了すると、スリーププロセスは引き続き実行され、その親PIDは1になります(_ps -ef_で確認)。

[更新] :

通常、信号Nがスクリプトにトラップされない場合、_kill -N script_PID_はスクリプトを直接強制終了します。ただし、SIGINTに注意してください。スクリプトがトラップしなくても、_kill -2 script_PID_は直接強制終了しません。スクリプトは、子プロセス(たとえば、sleep 10)が完了するまで待機します。ここで、script_PIDで複数のkill、つまり_kill -2 script_PID_ AND _kill -user-defined_trap_N script_PID_を実行すると仮定すると、次のようになります。

  1. スリープが10秒間待機した後、正常に戻る場合OR2以外の信号で強制終了 、スクリプトは、戻るときにキャッシュされたシグナル2を無視し、user-defined_trap_N関数を実行します。
  2. ただし、sleepkill by signal 2の場合、スクリプトは戻り時に組み込みのSIGINTハンドラーを実行し、user-defined_trap_Nを実行せずに直接killします。
5
林果皞

bashのマニュアルページ 状態

Bashがコマンドの完了を待機していて、トラップが設定されている信号を受信した場合、コマンドが完了するまでトラップは実行されません。

あなたの例では、trapsleep 100が完了するまで実行されません。

3
roaima