web-dev-qa-db-ja.com

JavaでSIGKILLシグナルを適切に処理する方法

プログラムがキルシグナルを受け取ったとき、どのようにクリーンアップを処理しますか?

たとえば、ログアウト時にサードパーティのアプリ(私のアプリ)がfinishコマンドを送信するように接続するアプリケーションがあります。アプリがkill -9で破壊されたときに、そのfinishコマンドを送信する最善の方法は何ですか?

編集1:キル-9はキャプチャできません。私を修正してくれてありがとう。

編集2:このケースは、ctrl-cと同じkillだけを呼び出す場合だと思います

105
Begui

SIGKILLを処理することは、あらゆるプログラムのあらゆる言語の impossible です。これにより、プログラムにバグや悪意がある場合でも、常にプログラムを終了することができます。ただし、プログラムを終了する手段はSIGKILLだけではありません。もう1つは、SIGTERMを使用することです。プログラムcanそのシグナルを処理します。プログラムshouldは、制御された、しかし迅速なシャットダウンを行うことでシグナルを処理します。コンピューターがシャットダウンすると、シャットダウンプロセスの最終段階で残りのすべてのプロセスにSIGTERMが送信され、それらのプロセスに数秒の猶予が与えられた後、SIGKILLが送信されます。

kill -9以外の other に対してこれを処理する方法は、 shutdown フックを登録することです。 ( [〜#〜] sigterm [〜#〜] kill -15を使用できる場合、シャットダウンフックは機能します。 ( [〜#〜] sigint [〜#〜] kill -2 [〜#〜] does [〜#〜] プログラムを正常に終了し、シャットダウンフックを実行します。

新しい仮想マシンのシャットダウンフックを登録します。

Java仮想マシンは、2種類のイベントに応答してシャットダウンします。

  • プログラムは、最後の非デーモンスレッドが終了するか、exit(同等にSystem.exit)メソッドが呼び出されると、正常に終了します。
  • 仮想マシンは、^ Cの入力などのユーザー割り込み、またはユーザーのログオフやシステムのシャットダウンなどのシステム全体のイベントに応答して終了します。

OSX 10.6.3およびkill -9で次のテストプログラムを試してみました。 [〜#〜] not [〜#〜] 期待どおりにシャットダウンフックを実行しました。 kill -15 [〜#〜] does [〜#〜] 毎回シャットダウンフックを実行します。

public class TestShutdownHook
{
    public static void main(String[] args) throws InterruptedException
    {
        Runtime.getRuntime().addShutdownHook(new Thread()
        {
            @Override
            public void run()
            {
                System.out.println("Shutdown hook ran!");
            }
        });

        while (true)
        {
            Thread.sleep(1000);
        }
    }
}

プログラムでkill -9を実際に適切に処理する方法はありません。

まれに、仮想マシンが異常終了する場合があります。つまり、正常にシャットダウンせずに実行を停止します。これは、たとえばUnixのSIGKILLシグナルやMicrosoft WindowsのTerminateProcess呼び出しなどで、仮想マシンが外部で終了したときに発生します。

kill -9を処理する唯一の本当のオプションは、別のウォッチャープログラムがメインプログラムを監視するか、ラッパースクリプトを使用することです。これを行うには、psコマンドをポーリングしてリスト内のプログラムを探し、消失したときにそれに応じて動作するシェルスクリプトを使用します。

#!/usr/bin/env bash

Java TestShutdownHook
wait
# notify your other app that you quit
echo "TestShutdownHook quit"
120
user177800

特定のJVMで独自のシグナルを処理する方法は、areです- HotSpot JVMに関するこの記事 を参照してください。

Sunの内部Sun.misc.Signal.handle(Signal, SignalHandler)メソッド呼び出しを使用すると、シグナルハンドラを登録することもできますが、JVMで使用されるINTTERMなどの信号はおそらく登録できません。

anyシグナルを処理できるようにするには、JVMからオペレーティングシステムの領域にジャンプする必要があります。

(たとえば)異常終了を検出するために通常行うことは、Perlスクリプト内でJVMを起動することですが、waitpidシステムコールを使用してJVMを待機させることです。

JVMが終了するたびに、JVMが終了した理由が通知され、必要なアクションを実行できます。

13

JVMが正常にinterruptsthread.interrupt() )アプリケーションによって作成されたすべての実行中のスレッド、少なくともシグナル SIGINT (kill -2) および SIGTERM (kill -15) の場合。

このように、シグナルはそれらに転送され、 標準的な方法 でのスレッドの取り消しとリソースの最終的なファイナライズを許可します。

しかし、これはそうではありません(少なくとも私のJVM実装では:Java(TM) SE Runtime Environment (build 1.8.0_25-b17), Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

他のユーザーがコメントしたように、shutdown hooksの使用は必須のようです。

だから、どのようにそれを処理しますか?

まず、すべてのプログラムでそれを気にしません。ユーザーのキャンセルと予期しない終了を追跡したいプログラムでのみです。たとえば、あなたのJavaプログラムは他者によって管理されているプロセスです。正常に終了したか(マネージャープロセスからのSIGTERM)またはシャットダウンかを区別することができます。 (起動時にジョブを自動的に再起動するため)が発生しました。

基礎として、私は常に長期実行スレッドに割り込み状態を定期的に認識させ、割り込みが発生するとInterruptedExceptionをスローします。これにより、開発者が制御する方法で実行のファイナライズが可能になります(標準のブロック操作と同じ結果が得られます)。次に、スレッドスタックの最上位でInterruptedExceptionがキャプチャされ、適切なクリーンアップが実行されます。これらのスレッドは、割り込み要求への応答方法を知るためにコーディングされています。高 凝集 デザイン。

そのため、これらの場合、シャットダウンフックを追加します。これは、JVMがデフォルトで実行するはずの処理を実行します。アプリケーションで作成され、実行中のすべての非デーモンスレッドに割り込みます。

Runtime.getRuntime().addShutdownHook(new Thread() {
    @Override
    public void run() {
        System.out.println("Interrupting threads");
        Set<Thread> runningThreads = Thread.getAllStackTraces().keySet();
        for (Thread th : runningThreads) {
            if (th != Thread.currentThread() 
                && !th.isDaemon() 
                && th.getClass().getName().startsWith("org.brutusin")) {
                System.out.println("Interrupting '" + th.getClass() + "' termination");
                th.interrupt();
            }
        }
        for (Thread th : runningThreads) {
            try {
                if (th != Thread.currentThread() 
                && !th.isDaemon() 
                && th.isInterrupted()) {
                    System.out.println("Waiting '" + th.getName() + "' termination");
                    th.join();
                }
            } catch (InterruptedException ex) {
                System.out.println("Shutdown interrupted");
            }
        }
        System.out.println("Shutdown finished");
    }
});

Githubの完全なテストアプリケーション: https://github.com/idelvall/kill-test

8
idelvall

Runtime.getRuntime().addShutdownHook(...)を使用できますが、いずれにしてもと呼ばれることを保証することはできません。

6
lexicore

Kill -9に対応する方法が1つあります。つまり、強制終了されるプロセスを監視し、必要に応じてクリーンアップする別のプロセスを用意することです。これにはおそらくIPCが関係し、かなりの作業が必要になります。両方のプロセスを同時に強制終了することで、それをオーバーライドできます。 。

-9でプロセスを強制終了した人は、理論的には彼/彼女が何をしているか、そしてそれが物事を矛盾した状態のままにする可能性があることを知っているはずです。

0
Arno Schäfer