web-dev-qa-db-ja.com

SIGTERMをBashの子に転送する

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

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

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

98
Lorenz

試してください:

#!/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を受信した場合、シェルは終了する前にSIGTERMトラップハンドラとして指定された_term関数を呼び出します(クリーンアップを実行し、killを使用して手動で信号をサーバープロセスに伝播します)。 。

103
cuonglm

BashはSIGTERMのようなシグナルを現在待機しているプロセスに転送しません。サーバーを推測してスクリプトを終了したい場合(サーバーを直接起動した場合と同様に、信号やその他を処理できるようにします)、 execを使用する必要があります。これにより、シェルが開かれているプロセスに置き換えられます

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

何らかの理由でシェルを維持する必要がある場合(つまり、サーバーの終了後にクリーンアップを行う必要がある場合)、trapwait、およびkillSensorSmithの回答 を参照してください。

83

Andreas Veithenが指摘 (OPの例のように)呼び出しから戻る必要がない場合は、execコマンドを介して呼び出すだけで十分です( @ Stuart P 。ベントレーの答え )。そうでなければ、「伝統的な」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
26
SensorSmith

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

trap 'kill -TERM $PID' TERM INT
$Java_EXECUTABLE $Java_ARGS &
PID=$!
wait $PID
trap - TERM INT
wait $PID
EXIT_STATUS=$?
4
user1463361