多くの外部プロセスを呼び出す大規模スクリプトにタイムアウトを実装しようとしています。
タイムアウトに達したことを毎秒確認するウォッチャーとして別のプロセスを開始します。
(
PARENT_PID=$$
((t = 2 ))
while ((t > 0)); do
sleep 1
((t -= 1))
done
kill -s ALRM ${PARENT_PID} 2> /dev/null
) &
その後、ALRM
メインスクリプトから信号をキャプチャします。
trap critical ALRM
最後に、キー機能からいくつかのテキストを印刷し、プログラムをきれいに終了します。
これで問題が発生します
- $ PARENT_PIDの実行中にタイムアウトが発生すると、すべてが期待どおりに機能します。
- 子プロセスの実行中にタイムアウトが発生した場合(たとえば、opensslを呼び出してopensslが実行されている場合)、何も起こりません。実行中の子プロセスを終了すると(たとえば、CRTL+キーを押してC)、親プロセストラップがアクティブになり、すべてが期待どおりに続行されます。
この問題をどのように解決できますか?これで親プロセスのトラップが呼び出され、プロセス全体を終了できますか?
修正する
ウォッチャーが-KILL
親プロセスを終了すると、すべての子プロセスを処理するプロセスが期待どおりに終了します。
問題は、タイムアウトを傍受していくつかの出力を生成したいということです。これにはALRM信号を使用します。
信号はKILL
無視できず、信号も無視できますALRM
。
現在私の考えは次のとおりです。
- 親プロセスには警告トラップがあります。
- 子プロセスは警告信号を無視します。
- シグナルが親プロセスに送信されると、子プロセスが実行され、親プロセスはバックグラウンドにあります。
- 子が終了すると、私のプロセスは目覚めます。
これが正しいなら、私の考えは決してうまくいきません。
それを機能させるには、各サブプロセスごとにする必要があります。
- バックグラウンドで始めましょう
- 待って続ける
これにより、親プロセスが信号をキャプチャできます。
私は正しいですか?
アップデート2
単純化された例
critical() {
printf "whathever\n"
WATCHDOG_PID=$!
kill -TERM $WATCHDOG_PID
}
(
PARENT_PID=$$
((t = "${TIMEOUT}" ))
while ((t > 0)); do
sleep 1
((t -= 1))
done
kill -s ALRM ${PARENT_PID} 2> /dev/null
) &
trap critical ALRM
# an example of a long running command
(echo 'Q' | openssl s_client -connect corti.li:imap -servername corti.li -verify 6 ) &
wait
WATCHDOG_PID=$!
kill -TERM $WATCHDOG_PID
openssl
ALRM信号が実行時に送信されると、何も起こりません。openssl
バックグラウンドでもトラップは呼び出されません。
答え1
完全な答えを得るには親のコードが必要ですが、コードが不足している場合に発生する可能性のあるヒントは次のとおりです。
オープングループ基本仕様6号 -セクション2.11
ユーティリティがフォアグラウンド命令の実行が完了するのを待っている間、シェルがトラップが確立されたという信号を受信すると、この信号に関連するトラップは、フォアグラウンドコマンドが完了するまで実行されません。
シェルが
wait
ユーティリティを介して非同期コマンドが完了するのを待っている間にトラップ設定信号を受信すると、ユーティリティはwait
すぐに終了ステータス> 128に戻り、その直後に関連するトラップが信号を受け取る必要があります。
(マイフォーマット)
wait
これは、親プロセスがコマンド以外のものを待っている場合、実行中の操作が完了するまで信号処理が遅れることを意味します。
wait
SIGALRMをすぐに受け取るように、両親に待つように依頼することができます。