Come gestire con grazia il segnale SIGKILL in Java


113

Come gestisci la pulizia quando il programma riceve un segnale di kill?

Ad esempio, esiste un'applicazione a cui mi connetto che desidera che qualsiasi app di terze parti (la mia app) invii un finishcomando quando si disconnette. Qual è il modo migliore per inviare quel finishcomando quando la mia app è stata distrutta con un kill -9?

modifica 1: kill -9 non può essere catturato. Grazie ragazzi per avermi corretto.

modifica 2: immagino che questo caso sarebbe quando quello chiama just kill che è lo stesso di ctrl-c


44
kill -9significa per me: "Vattene, fallo processo, via con te!", su cui il processo cesserà di essere. Subito.
ZoogieZork

11
Sulla maggior parte dei * nix di cui sono a conoscenza, kill -9 non può essere intercettato e gestito con garbo da nessun programma, indipendentemente dalla lingua in cui è stato scritto.
Presidente James K. Polk

2
@Begui: oltre a ciò che gli altri hanno commentato e risposto, SE il tuo sistema operativo Un x non si interrompe istantaneamente * E RIPRENDI TUTTE LE RISORSE utilizzate dal programma che è stato ucciso -9'ed , beh ... Il sistema operativo è rotto.
SyntaxT3rr0r

1
Riguardo al comando kill -9, la manpage dice più precisamente: "9 KILL (non-catchable, non-ignorable kill)". SIGKILL un segnale che viene gestito dal sistema operativo, non dall'applicazione.
user1338062

4
Semplicemente killnon è la stessa di Ctrl-C, poiché killsenza specificare quale segnale inviare invierà SIGTERM, mentre Ctrl-C invia SIGINT.
alesguzik

Risposte:


136

È impossibile per qualsiasi programma, in qualsiasi lingua, gestire un SIGKILL. È così che è sempre possibile terminare un programma, anche se il programma è difettoso o dannoso. Ma SIGKILL non è l'unico mezzo per terminare un programma. L'altro è usare un SIGTERM. I programmi possono gestire quel segnale. Il programma dovrebbe gestire il segnale eseguendo uno spegnimento controllato ma rapido. Quando un computer si spegne, la fase finale del processo di arresto invia a ogni processo rimanente un SIGTERM, concede a quei processi alcuni secondi di grazia, quindi invia loro un SIGKILL.

Il modo per gestirlo per qualcosa di diverso da quello kill -9sarebbe registrare un hook di arresto . Se puoi usare ( SIGTERM ) kill -15l'hook shutdown funzionerà. ( SIGINT ) kill -2 FA causa il programma di uscita con grazia ed eseguire i ganci di arresto.

Registra un nuovo hook di arresto della macchina virtuale.

La Java virtual machine si spegne in risposta a due tipi di eventi:

  • Il programma esce normalmente, quando esce l'ultimo thread non daemon o quando viene richiamato il metodo exit (equivalentemente, System.exit), o
  • La macchina virtuale viene terminata in risposta a un'interruzione dell'utente, come la digitazione di ^ C, o a un evento a livello di sistema, come la disconnessione dell'utente o l'arresto del sistema.

Ho provato il seguente programma di test su OSX 10.6.3 e su di kill -9esso NON è stato eseguito l'hook di arresto, come previsto. Su un kill -15esso NON eseguire il gancio di arresto ogni volta.

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

Non c'è alcun modo per gestire kill -9in modo davvero grazioso un in nessun programma.

In rari casi la macchina virtuale può interrompersi, ovvero interrompere l'esecuzione senza spegnersi in modo pulito. Ciò si verifica quando la macchina virtuale viene terminata esternamente, ad esempio con il segnale SIGKILL su Unix o la chiamata TerminateProcess su Microsoft Windows.

L'unica vera opzione per gestire un kill -9è avere un altro programma watcher che controlla la scomparsa del programma principale o utilizza uno script wrapper. Potresti farlo con uno script di shell che interroghi il pscomando cercando il tuo programma nell'elenco e agisca di conseguenza quando è scomparso.

#!/usr/bin/env bash

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

12

Ci sono modi per gestire i propri segnali in alcuni JVM - vedi questo articolo sulla JVM HotSpot per esempio.

Usando la sun.misc.Signal.handle(Signal, SignalHandler)chiamata al metodo interno di Sun si è anche in grado di registrare un gestore di segnali, ma probabilmente non per segnali come INTo TERMcome vengono usati dalla JVM.

Per essere in grado di gestire qualsiasi segnale dovresti saltare fuori dalla JVM e nel territorio del sistema operativo.

Quello che generalmente faccio per (ad esempio) rilevare una terminazione anomala è avviare la mia JVM all'interno di uno script Perl, ma fare in modo che lo script attenda la JVM utilizzando la waitpidchiamata di sistema.

Vengo quindi informato ogni volta che la JVM esce e perché è terminata e posso intraprendere l'azione necessaria.


3
Nota che puoi acquisire INTe TERMcon sun.misc.Signal, ma non puoi gestirlo QUITperché la JVM lo riserva per il debug, né KILLperché il sistema operativo terminerà immediatamente la JVM. Il tentativo di gestire uno dei due solleverà un IllegalArgumentException.
dimo414

12

Mi aspetto che la JVM interrompa con grazia ( thread.interrupt()) tutti i thread in esecuzione creati dall'applicazione, almeno per i segnali SIGINT (kill -2)e SIGTERM (kill -15).

In questo modo, il segnale verrà inoltrato a loro, consentendo un'elegante cancellazione del thread e la finalizzazione delle risorse nei modi standard .

Ma questo non è il caso (almeno nella mia implementazione 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).

Come hanno commentato altri utenti, l'uso di hook di spegnimento sembra obbligatorio.

Quindi, come lo gestirò?

Ebbene prima di tutto, non mi interessa in tutti i programmi, solo in quelli in cui voglio tenere traccia delle cancellazioni degli utenti e dei fini imprevisti. Ad esempio, immagina che il tuo programma java sia un processo gestito da altri. Potresti voler distinguere se è stato terminato normalmente ( SIGTERMdal processo di gestione) o si è verificato un arresto (per riavviare automaticamente il lavoro all'avvio).

Come base, rendo sempre i miei thread di lunga durata periodicamente consapevoli dello stato di interruzione e lancio un InterruptedExceptionse interrotto. Ciò consente la finalizzazione dell'esecuzione in modo controllato dallo sviluppatore (producendo anche lo stesso risultato delle operazioni di blocco standard). Quindi, al livello superiore dello stack di thread, InterruptedExceptionviene acquisito e viene eseguita la pulizia appropriata. Questi thread sono codificati per sapere come rispondere a una richiesta di interruzione. Design ad alta coesione .

Quindi, in questi casi, aggiungo un hook di spegnimento, che fa quello che penso che la JVM dovrebbe fare per impostazione predefinita: interrompere tutti i thread non daemon creati dalla mia applicazione che sono ancora in esecuzione:

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

Applicazione di prova completa su github: https://github.com/idelvall/kill-test


6

Puoi usare Runtime.getRuntime().addShutdownHook(...), ma non puoi essere sicuro che verrà chiamato in ogni caso .


12
Ma nel caso di kill -9 quasi certamente non funzionerà.
Presidente James K. Polk

1

C'è un modo per reagire a un kill -9: cioè avere un processo separato che monitora il processo che viene terminato e lo ripulisce se necessario. Ciò probabilmente implicherebbe IPC e sarebbe un bel po 'di lavoro, e puoi ancora sovrascriverlo uccidendo entrambi i processi allo stesso tempo. Presumo che nella maggior parte dei casi non ne varrà la pena.

Chiunque uccida un processo con -9 dovrebbe teoricamente sapere cosa sta facendo e che potrebbe lasciare le cose in uno stato incoerente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.