SIGTERMをBashの子に転送する


86

次のようなBashスクリプトがあります。

#!/bin/bash
echo "Doing some initial work....";
/bin/start/main/server --nodaemon

これで、スクリプトを実行しているbashシェルがSIGTERMシグナルを受信した場合、実行中のサーバーにSIGTERMも送信する必要があります(ブロックされるため、トラップは不可能です)。それは可能ですか?

回答:


91

試してください:

#!/bin/bash 

_term() { 
  echo "Caught SIGTERM signal!" 
  kill -TERM "$child" 2>/dev/null
}

trap _term SIGTERM

echo "Doing some initial work...";
/bin/start/main/server --nodaemon &

child=$! 
wait "$child"

通常、bash子プロセスの実行中はシグナルを無視します。でサーバーを起動する&ことがで、シェルのジョブ制御システムへの意志の背景$!(で使用するサーバーのPIDを保持waitしてkill)。呼び出しwaitは、指定されたPID(サーバー)のジョブが終了するのを待つか、信号が発せられるのを待ちます。

シェルが受信するとSIGTERM(またはサーバーが独立して終了するwaitと)、呼び出しは戻ります(サーバーの終了コードで終了するか、シグナルを受信した場合はシグナル番号+ 128で終了します)。その後、シェルがSIGTERMを受信した場合、_term終了する前にSIGTERMトラップハンドラとして指定された関数を呼び出します(クリーンアップを実行し、を使用して信号をサーバープロセスに手動で伝達しますkill)。


いいね!私はそれを試して、それをテストしたときに応答します。
ローレンツ14

7
しかし、execはシェルを与えられたプログラム置き換えます、なぜその後のwait呼び出しが必要なのか分かりませんか?
iruvar

5
1_CRのポイントは有効だと思います。単純に使用するexec /bin/start/main/server --nodaemon(この場合、シェルプロセスはサーバープロセスに置き換えられ、シグナルを伝播する必要はありません)か、を使用しますが/bin/start/main/server --nodaemon &exec実際には意味がありません。
アンドレアスヴェイテン

2
子が終了した後にのみシェルスクリプトを終了_term()する場合は、関数内でwait "$child"再度実行する必要があります。これは、シェルスクリプトが終了するのを待ってから再起動する他の監視プロセスがある場合、またはEXIT何らかのクリーンアップを行うためにトラップして、子プロセスが終了した後にのみ実行するようにニードする場合に必要になることがあります。
-LeoRochael

1
@AlexanderMills他の回答を読んでください。あなたが探しているかexecトラップ設定したいです。
スチュアートP.ベントレー

78

Bashは、SIGTERMなどのシグナルを、現在待機中のプロセスに転送しません。サーバーに接続してスクリプトを終了する場合(サーバーを直接起動した場合のように、信号やその他のものを処理できるようにする)、を使用してexecシェルを開いているプロセスに置き換えます

#!/bin/bash
echo "Doing some initial work....";
exec /bin/start/main/server --nodaemon

あなたには、いくつかの理由(すなわち。あなたは、サーバーが終了した後、いくつかのクリーンアップを行う必要がある)の周りにシェルを維持する必要がある場合は、の組み合わせを使用する必要がありtrapwaitkillSensorSmithの回答を参照してください。


これは正解です!はるかに簡潔で、OPの元の質問に正確に対応
BrDaHa

20

Andreas Veithenは、(OPの例のように)呼び出しから戻る必要がない場合は、単にexecコマンドで呼び出すだけで十分であると指摘します@Stuart P. Bentleyの答え)。それ以外の場合、「従来の」trap 'kill $CHILDPID' TERM(@cuonglmの答え)は開始ですがwait、トラップハンドラーの実行後に呼び出しが実際に戻りますが、子プロセスが実際に終了する前である可能性があります。そのため、「余分な」呼び出しを行うwaitことをお勧めします(@ user1463361の回答)。

これは改善されていますが、まだ競合状態になっています。つまり、シグナルがTERMシグナルの送信を再試行しない限り、プロセスが終了することはありません。脆弱性の窓は、トラップハンドラの登録と子供のPIDの記録の間です。

以下は、その脆弱性(再利用のために機能にパッケージ化されています)を排除します。

prep_term()
{
    unset term_child_pid
    unset term_kill_needed
    trap 'handle_term' TERM INT
}

handle_term()
{
    if [ "${term_child_pid}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null
    else
        term_kill_needed="yes"
    fi
}

wait_term()
{
    term_child_pid=$!
    if [ "${term_kill_needed}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null 
    fi
    wait ${term_child_pid}
    trap - TERM INT
    wait ${term_child_pid}
}

# EXAMPLE USAGE
prep_term
/bin/something &
wait_term

2
優れた仕事-私は(この上は、より包括的なソリューションであることに、私はまだ少しはStackExchange UIがためcuonglmの答えで私を信用しないことにうんざりだ、ここで指すように私の答えにリンクを更新したにスクリプトを固定します実際に、それはになっています何ほとんどすべての説明文書い OP後にも理解していなかった)いくつかのマイナーな再編集を行っています。
スチュアートP.ベントレー

2
@ StuartP.Bentley、ありがとう。これに2つの(受け入れられない)回答と外部参照が必要であること驚いたので、競合状態に陥らなければなりませんでした。私は、リンクへの参照を、追加できる小さな賞賛としてアップグレードします。
SensorSmith

3

待機コマンドが実際に終了する前にプロセスが強制終了されたため、提供されたソリューションが機能しません。その記事http://veithen.github.io/2014/11/16/sigterm-propagation.htmlを見つけました。アプリケーションの私の場合、最後のスニペットは、カスタムshランナーを使用してOpenShiftで起動しました。shプロセスが必要なのは、JavaプロセスのPIDが1の場合には不可能なスレッドダンプを取得する必要があるためです。

trap 'kill -TERM $PID' TERM INT
$JAVA_EXECUTABLE $JAVA_ARGS &
PID=$!
wait $PID
trap - TERM INT
wait $PID
EXIT_STATUS=$?
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.