Saltar al contenido

Cómo manejar con gracia la señal SIGKILL en Java

Te damos la solución a esta interrogante, al menos eso esperamos. Si tienes dudas puedes dejarlo en el apartado de comentarios y sin tardanza

Solución:

Está imposible para que cualquier programa, en cualquier idioma, maneje un SIGKILL. Esto es para que siempre sea posible finalizar un programa, incluso si el programa tiene errores o es malicioso. Pero SIGKILL no es el único medio para finalizar un programa. La otra es utilizar un SIGTERM. Programas poder manejar esa señal. El programa deberían manejar la señal haciendo un apagado controlado, pero rápido. Cuando una computadora se apaga, la etapa final del proceso de apagado envía a todos los procesos restantes un SIGTERM, les da a esos procesos unos segundos de gracia y luego les envía un SIGKILL.

La forma de manejar esto para cualquier cosa. otro que kill -9 sería registrar un gancho de cierre. Si puede usar (SIGTERM) kill -15 el gancho de apagado funcionará. (SIGNO) kill -2LO HACE hacer que el programa salga elegantemente y ejecute los ganchos de cierre.

Registra un nuevo gancho de cierre de máquina virtual.

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

  • El programa sale normalmente, cuando sale el último subproceso que no es un demonio o cuando se invoca el método de salida (equivalentemente, System.exit), o
  • La máquina virtual finaliza en respuesta a una interrupción del usuario, como al escribir ^ C, o un evento en todo el sistema, como el cierre de sesión del usuario o el apagado del sistema.

Probé el siguiente programa de prueba en OSX 10.6.3 y en kill -9 lo hizo NO ejecute el gancho de cierre, como se esperaba. En un kill -15 eso LO HACE ejecute el gancho de apagado cada vez.

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

No hay forma de manejar con gracia un kill -9 en cualquier programa.

En raras circunstancias, la máquina virtual puede abortar, es decir, dejar de funcionar sin apagarse limpiamente. Esto ocurre cuando la máquina virtual se termina 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 hacer que otro programa de vigilancia esté atento a que su programa principal desaparezca o utilice un script de envoltura. Podrías hacerlo con un script de shell que sondeara ps comando buscando su programa en la lista y actúe en consecuencia cuando desaparezca.

#!/usr/bin/env bash

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

Esperaría que la JVM con gracia interrumpe (thread.interrupt()) todos los hilos en ejecución creados por la aplicación, al menos para señales SIGINT (kill -2) y SIGTERM (kill -15).

De esta manera, la señal se les enviará, lo que permitirá una elegante cancelación de subprocesos y finalización de recursos en las formas estándar.

Pero este no es el caso (al menos en mi implementación de 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 comentaron otros usuarios, el uso de ganchos de cierre parece obligatorio.

Entonces, ¿cómo lo manejaría?

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

Como base, siempre hago que mis subprocesos de larga duración sean conscientes periódicamente del estado interrumpido y lanzo un InterruptedException si interrumpieron. Esto permite la finalización de la ejecución de forma 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 subprocesos, InterruptedException se captura y se realiza la limpieza adecuada. Estos hilos están codificados para saber cómo responder a una solicitud de interrupción. Diseño de alta cohesión.

Entonces, en estos casos, agrego un gancho de apagado, que hace lo que creo que la JVM debería hacer de forma predeterminada: interrumpir todos los subprocesos que no son demonios creados por mi aplicación que aún se están ejecutando:

Runtime.getRuntime().addShutdownHook(new Thread() 
    @Override
    public void run() 
        System.out.println("Interrupting threads");
        Set 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");
    
);

Solicitud de prueba completa en github: https://github.com/idelvall/kill-test

Ahí son formas de manejar sus propias señales en ciertas JVM: consulte este artículo sobre HotSpot JVM, por ejemplo.

Utilizando el sol interno sun.misc.Signal.handle(Signal, SignalHandler) llamada al método, también puede registrar un manejador de señales, pero probablemente no para señales como INT o TERM tal como los utiliza la JVM.

Para poder manejar ningún señal de que tendría que salir de la JVM y entrar en el territorio del sistema operativo.

Lo que generalmente hago para (por ejemplo) detectar una terminación anormal es lanzar mi JVM dentro de un script de Perl, pero hacer que el script espere a que el JVM use el waitpid llamada al sistema.

Luego se me informa cada vez que sale la JVM, y por qué salió, y puedo tomar las medidas necesarias.

Acuérdate de que tienes concesión de añadir una estimación objetiva si diste con el resultado.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *