118

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

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

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

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

4

6 に答える 6

152

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

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

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

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

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

OSX 10.6.3 で次のテスト プログラムを試しましたが、期待どおりにシャットダウン フックが実行されませんkill -9でした。毎回シャットダウンフックを実行します。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どのプログラムでもa を本当に適切に処理する方法はありません。

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

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

#!/usr/bin/env bash

java TestShutdownHook
wait
# notify your other app that you quit
echo "TestShutdownHook quit"
于 2010-03-29T22:10:23.253 に答える
14

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

于 2015-11-18T11:09:26.467 に答える
12

特定のJVMで独自のシグナルを処理する方法があります。たとえば、HotSpotJVMに関するこの記事を参照てください

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

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

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

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

于 2010-03-29T23:00:00.870 に答える
7

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

于 2010-03-29T22:11:10.990 に答える
2

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

-9 でプロセスを強制終了する人は誰でも、理論的には自分が何をしているかを知っている必要があり、一貫性のない状態になる可能性があります。

于 2017-08-08T15:39:03.147 に答える