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


113

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

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

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

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


44
kill -9私にとっての意味:「立ち去った、汚いプロセス、あなたと離れて!」すぐに。
ZoogieZork

11
。私は、キルの意識だと*最もnixesに-9どんな言語それがで書かれていなかった任意のプログラムによって処理優雅に傍受し、することはできません
社長ジェームズ・ポーク

2
@Begui:他の人がコメントと答えたものに加えて、IFあなた国連はOSが即座*殺す、すべてのリソースが回復しないxはプログラムされて使用キルは-9'edも、... OSが壊れています。
SyntaxT3rr0r

1
kill -9コマンドについては、マンページに「9 KILL(キャッチ不可、無視不可キル)」とより正確に記載されています。SIGKILLは、アプリケーションではなくOSによって処理される信号です。
user1338062

4
ジャストはkillとしては、Ctrl-Cと同じではありませんkillCtrlキーを押しながらCがSIGINTを送るのに対し、送信するためにどの信号を指定せずに、SIGTERMを送信します。
alesguzik

回答:


136

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

これ以外の場合にこれを処理する方法はkill -9シャットダウンフックを登録することです。(SIGTERM)を使用できる場合はkill -15、シャットダウンフックが機能します。(SIGINTkill -2 DOES正常に終了するプログラム原因とシャットダウンフックを実行します。

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

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

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

OSX 10.6.3で次のテストプログラムを試しましたが、期待どおりシャットダウンフックが実行されませんkill -9でした。で、それはDOESシャットダウンフックを毎回実行します。kill -15

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"

12

あります、特定のJVMで独自の信号を処理するための方法は-を参照のHotSpot JVMについては、この記事の例のために。

日曜内部使用してsun.misc.Signal.handle(Signal, SignalHandler)メソッド呼び出しを、あなたはまた、シグナルハンドラを登録することができ、おそらくないような信号のためのものであるINTか、TERM彼らはJVMによって使用されているとして。

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

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

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


3
あなたが注意することができますキャプチャINTTERMsun.misc.Signalていますが、処理できないQUITデバッグするためのJVMが埋蔵しているので、またKILLOSはすぐにJVMを終了しますので。いずれかを処理しようとすると、が発生しIllegalArgumentExceptionます。
dimo414 2015

12

JVM は、少なくともシグナルに対して、アプリケーションによって作成された実行中のすべてのスレッドを正常に中断するthread.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)

他のユーザーがコメントしたように、シャットダウンフックの使用は必須のようです。

それで、どうすればそれを処理できますか?

最初に、すべてのプログラムでそれを気にしません。ユーザーのキャンセルと予期しない終了を追跡したいプログラムでのみです。たとえば、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


6

使用できRuntime.getRuntime().addShutdownHook(...)ますが、どのような場合でも呼び出されることは保証されません。


12
しかし、kill -9の場合、ほぼ確実に実行されません。
James K. Polk大統領

1

kill -9に対応する1つの方法があります。それは、killされているプロセスを監視し、必要に応じてその後にクリーンアップする別のプロセスを用意することです。これにはおそらくIPCが含まれ、かなりの作業が必要になるため、両方のプロセスを同時に強制終了することで、IPCをオーバーライドできます。私はそれがほとんどの場合トラブルの価値がないと思います。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.