2010-03-29 10 views
84

¿Cómo maneja la limpieza cuando el programa recibe una señal de muerte?Cómo manejar con gracia la señal SIGKILL en Java

Por ejemplo, hay una aplicación a la que me conecto que quiere que cualquier aplicación de terceros (mi aplicación) envíe un comando finish al cerrar la sesión. ¿Cuál es la mejor opción para enviar ese comando finish cuando mi aplicación se ha destruido con un kill -9?

editar 1: kill -9 no se puede capturar. Gracias chicos por corregirme.

editar 2: Creo que este caso sería cuando el que llama simplemente matar, que es el mismo que ctrl-c

+30

'kill -9' significa para mí:" Begone, foul process, away withe! ", Sobre el cual el proceso dejará de ser. Inmediatamente. – ZoogieZork

+7

En la mayoría de los * nixes de los que tengo conocimiento, kill -9 no puede ser interceptado y manejado con gracia por ningún programa, sin importar en qué idioma esté escrito. –

+1

@Begui: además de lo que otros comentaron y respondieron, * IF * su sistema operativo Un * x no mata al instante * Y RECUPERAR TODOS LOS RECURSOS * utilizados por el programa * kill -9'ed *, bueno ... El sistema operativo está roto. – SyntaxT3rr0r

Respuesta

94

La manera de manejar esto para nada otra que kill -9 sería registrar un gancho shutdown . Si puede usar (SIGTERM) kill -15, el gancho de apagado funcionará. (SIGINT) kill -2DOES causa que el programa salga y ejecute correctamente los ganchos de apagado.

Registra una nueva máquina virtual shutdown hook.

La máquina virtual Java se apaga en respuesta a dos tipos de eventos:

* The program exits normally, when the last non-daemon thread exits or 

cuando la salida (equivalentemente, System.exit) se invoca el método, o

* The virtual machine is terminated in response to a user 

de interrupción , como escribir^C, o un evento de todo el sistema , como el cierre de sesión de usuario o el cierre del sistema.

He intentado lo siguiente programa de pruebas en OSX 10.6.3 y en kill -9 lo hizo NO ejecutar el gancho de cierre, no creo que lo haría. En un kill -15DOES ejecuta el gancho de apagado cada vez.

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

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

no hay ninguna manera de manejar con gracia realmente un kill -9 en cualquier programa.

En raras ocasiones el máquina virtual puede abortar, es decir, dejar de funcionando sin necesidad de apagar limpiamente. Esto ocurre cuando la máquina virtual finaliza externamente, por ejemplo con la señal SIGKILL en Unix o la llamada TerminateProcess en Microsoft Windows.

La única opción real para manejar un kill -9 es tener otro reloj Relojes programa para su programa principal que se fuera o utilizar un guión envoltorio. Puede hacer esto con un script de shell que sondeó el comando ps buscando su programa en la lista y actuando en consecuencia cuando desapareció.

#!/bin/bash 

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

Puede utilizar Runtime.getRuntime().addShutdownHook(...), pero no se puede garantizar que se va a llamar en cualquier caso .

+5

Pero en el caso de kill -9 es casi seguro que no se ejecutará. –

12

Hay son formas de manejar sus propias señales en ciertas JVM; consulte this article about the HotSpot JVM por ejemplo.

Al utilizar el método interno sun.misc.Signal.handle(Signal, SignalHandler) Sun llamada que también es capaz de registrar un manejador de señales, pero probablemente no para señales como INT o TERM ya que son utilizados por la JVM.

Para poder manejar cualquier señal que debería saltar de la JVM al territorio del sistema operativo.

Lo que generalmente hago para (por ejemplo) detectar una terminación anormal es iniciar mi JVM dentro de una secuencia de comandos de Perl, pero hacer que la secuencia de comandos espere la JVM usando la llamada al sistema waitpid.

Me informan siempre que sale la JVM, y por qué salió, y pueden tomar las medidas necesarias.

+0

Tenga en cuenta que * puede * capturar 'INT' y' TERM' con 'sun.misc.Signal', pero no puede manejar' QUIT' porque la JVM lo reserva para la depuración, ni 'KILL' porque el sistema operativo terminará la JVM inmediatamente. Intentar manejar cualquiera de ellos generará una 'IllegalArgumentException'. – dimo414

4

Yo esperaría que la JVM con gracia interrumpe (thread.interrupt()) todos los subprocesos que se ejecutan creados por la aplicación, al menos para las señales SIGINT (kill -2) y SIGTERM (kill -15).

De esta manera, la señal se les enviará, lo que permite una cancelación de hilo elegante y la finalización de recursos en el standard ways.

Pero este no es el caso (al menos en mi aplicación 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)

como se ha comentado otros usuarios, el uso de ganchos de cierre parece obligatoria

Así que, ¿cómo puedo lo manejaría?

Bueno, no me importa en todos los programas, solo en aquellos en los que quiero hacer un seguimiento de cancelaciones de usuarios y fines inesperados. Por ejemplo, imagine que su programa Java es un proceso hombre envejecido por otro. Es posible que desee diferenciar si ha finalizado correctamente (SIGTERM desde el proceso del administrador) o si se ha producido un apagado (para reiniciar automáticamente el trabajo al inicio).

Como base, siempre hago que mis hilos de ejecución prolongada sean conscientes periódicamente del estado interrumpido y lanzo un InterruptedException si se interrumpen. Esto permite la finalización de la ejecución de una manera controlada por el desarrollador (que también produce el mismo resultado que las operaciones de bloqueo estándar). Luego, en el nivel superior de la pila de hilos, se captura InterruptedException y se realiza una limpieza adecuada. Estos hilos están codificados para saber cómo responder a una solicitud de interrupción. Alto cohesion diseño.

Por lo tanto, en estos casos, agrego un gancho de cierre, que hace lo que yo creo que la JVM debe hacer de forma predeterminada: interrumpir todos los hilos no demonio creados por mi solicitud que todavía se están ejecutando:

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

Aplicación de prueba completa en github: https://github.com/idelvall/kill-test

0

Hay una forma de reaccionar a un kill -9: es tener un proceso separado que supervisa el proceso que se está matando y lo limpia después si es necesario. Esto probablemente involucre a IPC y sería bastante trabajo, y aún puede anularlo matando ambos procesos al mismo tiempo. Supongo que no valdrá la pena en la mayoría de los casos.

Quien mata un proceso con -9 debería saber teóricamente qué está haciendo y que puede dejar las cosas en un estado incoherente.

Cuestiones relacionadas