Wie man das SIGKILL-Signal in Java elegant handhabt


113

Wie gehen Sie mit der Bereinigung um, wenn das Programm ein Kill-Signal empfängt?

Beispielsweise gibt es eine Anwendung, mit der ich eine Verbindung herstelle und die möchte, dass eine Drittanbieter-App (meine App) finishbeim Abmelden einen Befehl sendet . Was ist das beste Wort, um diesen finishBefehl zu senden, wenn meine App mit einem zerstört wurde kill -9?

edit 1: kill -9 kann nicht erfasst werden. Danke Jungs, dass ihr mich korrigiert habt.

edit 2: Ich denke, dieser Fall wäre, wenn derjenige, der gerade kill aufruft, dasselbe ist wie Strg-C


44
kill -9bedeutet für mich: "Beginne, fauler Prozess, weg mit dir!", worauf der Prozess aufhören wird zu sein. Sofort.
ZoogieZork

11
Bei den meisten mir bekannten Nixen kann kill -9 von keinem Programm abgefangen und ordnungsgemäß behandelt werden, unabhängig davon, in welcher Sprache es geschrieben wurde.
Präsident James K. Polk

2
@Begui: Zusätzlich zu dem, was die anderen kommentiert und beantwortet haben, WENN Ihr Un x-Betriebssystem nicht sofort tötet * UND ALLE RESSOURCEN WIEDERHERSTELLT , die von dem Programm verwendet werden , das getötet wird , ist das Betriebssystem defekt.
SyntaxT3rr0r

1
Über den Befehl kill -9 heißt es auf der Manpage genauer: "9 KILL (nicht fangbarer, nicht ignorierbarer Kill)". SIGKILL ein Signal, das vom Betriebssystem verarbeitet wird, nicht von der Anwendung.
user1338062

4
Just killist nicht dasselbe wie Strg-C, da killohne Angabe des zu sendenden Signals SIGTERM gesendet wird, während Strg-C SIGINT sendet.
Alesguzik

Antworten:


136

Es ist für jedes Programm in jeder Sprache unmöglich , mit einem SIGKILL umzugehen. Auf diese Weise ist es immer möglich, ein Programm zu beenden, auch wenn das Programm fehlerhaft oder böswillig ist. SIGKILL ist jedoch nicht das einzige Mittel, um ein Programm zu beenden. Das andere ist, ein SIGTERM zu verwenden. Programme können dieses Signal verarbeiten. Das Programm sollte das Signal durch kontrolliertes, aber schnelles Herunterfahren verarbeiten. Wenn ein Computer heruntergefahren wird, sendet die letzte Phase des Herunterfahrvorgangs jedem verbleibenden Prozess ein SIGTERM, gibt diesen Prozessen einige Sekunden Zeit und sendet ihnen dann ein SIGKILL.

Die Möglichkeit, dies für etwas anderes zu handhaben, als kill -9einen Shutdown- Hook zu registrieren . Wenn Sie ( SIGTERM ) verwenden kill -15können, funktioniert der Shutdown-Hook. ( SIGINT ) kill -2 bewirkt, dass das Programm die Shutdown-Hooks ordnungsgemäß beendet und ausführt .

Registriert einen neuen Hook zum Herunterfahren der virtuellen Maschine.

Die Java Virtual Machine wird als Reaktion auf zwei Arten von Ereignissen heruntergefahren:

  • Das Programm wird normal beendet, wenn der letzte Nicht-Daemon-Thread beendet wird oder wenn die Methode exit (entsprechend System.exit) aufgerufen wird, oder
  • Die virtuelle Maschine wird als Reaktion auf einen Benutzerinterrupt wie die Eingabe von ^ C oder ein systemweites Ereignis wie die Benutzerabmeldung oder das Herunterfahren des Systems beendet.

Ich habe das folgende Testprogramm unter OSX 10.6.3 ausprobiert und kill -9darauf den Shutdown-Hook NICHT wie erwartet ausgeführt. Auf einem kill -15es DOES das Herunterfahren Haken jedes Mal ausgeführt werden .

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);
        }
    }
}

Es gibt keine Möglichkeit, kill -9in einem Programm wirklich elegant mit einem umzugehen.

In seltenen Fällen kann die virtuelle Maschine abgebrochen werden, dh die Ausführung wird beendet, ohne dass das System ordnungsgemäß heruntergefahren wird. Dies tritt auf, wenn die virtuelle Maschine extern beendet wird, z. B. mit dem SIGKILL-Signal unter Unix oder dem TerminateProcess-Aufruf unter Microsoft Windows.

Die einzige echte Option, um mit a kill -9umzugehen, besteht darin, ein anderes Watcher-Programm zu überwachen, damit Ihr Hauptprogramm verschwindet oder ein Wrapper-Skript verwendet. Sie können dies mit einem Shell-Skript tun, das den psBefehl abfragt, der in der Liste nach Ihrem Programm sucht, und entsprechend handelt, wenn es verschwindet.

#!/usr/bin/env bash

java TestShutdownHook
wait
# notify your other app that you quit
echo "TestShutdownHook quit"

12

Es gibt Möglichkeiten, Ihre eigenen Signale in bestimmten JVMs zu verarbeiten - siehe diesen Artikel zum Beispiel über die HotSpot-JVM .

Mit dem internen sun.misc.Signal.handle(Signal, SignalHandler)Methodenaufruf von Sun können Sie auch einen Signalhandler registrieren, wahrscheinlich jedoch nicht für Signale wie INToder TERMwie sie von der JVM verwendet werden.

Um ein Signal verarbeiten zu können, müssten Sie aus der JVM in das Gebiet des Betriebssystems springen.

Um (zum Beispiel) eine abnormale Beendigung zu erkennen, starte ich meine JVM im Allgemeinen in einem Perl-Skript, lasse das Skript jedoch mithilfe des waitpidSystemaufrufs auf die JVM warten .

Ich werde dann informiert, wann immer die JVM beendet wird und warum sie beendet wurde, und kann die erforderlichen Maßnahmen ergreifen.


3
Beachten Sie können erfassen INTund TERMmit sun.misc.Signal, aber man kann nicht damit umgehen , QUITweil die JVM behält es für das Debuggen, noch , KILLweil das Betriebssystem die JVM sofort beendet wird. Wenn Sie versuchen, mit beiden umzugehen, wird ein IllegalArgumentException.
dimo414

12

Ich würde erwarten, dass die JVM alle von der Anwendung erstellten laufenden Threads ordnungsgemäß unterbricht ( thread.interrupt()), zumindest für Signale SIGINT (kill -2)und SIGTERM (kill -15).

Auf diese Weise wird das Signal an sie weitergeleitet, was eine ordnungsgemäße Thread-Löschung und Ressourcen-Finalisierung auf die übliche Weise ermöglicht .

Dies ist jedoch nicht der Fall (zumindest in meiner JVM-Implementierung : Java(TM) SE Runtime Environment (build 1.8.0_25-b17), Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode).

Wie andere Benutzer kommentierten, scheint die Verwendung von Shutdown-Hooks obligatorisch zu sein.

Wie würde ich damit umgehen?

Zunächst einmal ist es mir nicht in allen Programmen wichtig, nur in denen, in denen ich Benutzerabbrüche und unerwartete Ziele verfolgen möchte. Stellen Sie sich zum Beispiel vor, Ihr Java-Programm ist ein Prozess, der von anderen verwaltet wird. Möglicherweise möchten Sie unterscheiden, ob es ordnungsgemäß beendet wurde ( SIGTERMvom Manager-Prozess) oder ob ein Herunterfahren stattgefunden hat (um den Job beim Start automatisch neu zu starten).

Als Basis mache ich meine lang laufenden Threads immer regelmäßig auf den unterbrochenen Status aufmerksam und werfe einen, InterruptedExceptionwenn sie unterbrochen werden. Dies ermöglicht die Finalisierung der Ausführung auf eine vom Entwickler kontrollierte Weise (und liefert auch das gleiche Ergebnis wie Standardblockierungsvorgänge). Dann wird auf der obersten Ebene des Thread-Stapels InterruptedExceptionerfasst und eine entsprechende Bereinigung durchgeführt. Diese Threads sind so codiert, dass sie wissen, wie sie auf eine Unterbrechungsanforderung reagieren sollen. Design mit hohem Zusammenhalt .

In diesen Fällen füge ich einen Shutdown-Hook hinzu, der genau das tut, was die JVM meiner Meinung nach standardmäßig tun sollte: Unterbrechen Sie alle von meiner Anwendung erstellten Nicht-Daemon-Threads, die noch ausgeführt werden:

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");
    }
});

Vollständige Testanwendung bei github: https://github.com/idelvall/kill-test


6

Sie können verwenden Runtime.getRuntime().addShutdownHook(...), aber Sie können nicht garantieren, dass es auf jeden Fall aufgerufen wird .


12
Aber im Fall von kill -9 läuft es mit ziemlicher Sicherheit nicht.
Präsident James K. Polk

1

Es gibt eine Möglichkeit, auf einen Kill -9 zu reagieren: einen separaten Prozess, der den getöteten Prozess überwacht und bei Bedarf bereinigt. Dies würde wahrscheinlich IPC beinhalten und wäre ziemlich viel Arbeit, und Sie können es trotzdem überschreiben, indem Sie beide Prozesse gleichzeitig beenden. Ich gehe davon aus, dass sich die Mühe in den meisten Fällen nicht lohnt.

Wer einen Prozess mit -9 beendet, sollte theoretisch wissen, was er / sie tut und dass dies zu einem inkonsistenten Zustand führen kann.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.