java sigkill

Cómo manejar elegantemente la señal SIGKILL en Java



(5)

¿Cómo manejas la limpieza cuando el programa recibe una señal de muerte?

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 de finish al cerrar la sesión. ¿Cuál es la mejor opción para enviar ese comando de finish cuando mi aplicación ha sido destruida con kill -9 ?

edición 1: kill -9 no se puede capturar. Gracias chicos por corregirme.

edición 2: supongo que este caso sería cuando el que llama acaba de matar, que es lo mismo que ctrl-c


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

De esta forma, la señal se enviará a ellos, lo que permite una cancelación elegante de subprocesos y la finalización de recursos de 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 otros usuarios comentaron, el uso de los ganchos de cierre parece obligatorio.

Entonces, ¿cómo lo manejaría?

Bueno, en primer lugar, no me importa en todos los programas, solo en aquellos en los que deseo hacer un seguimiento de las cancelaciones de usuarios y los extremos inesperados. Por ejemplo, imagine que su programa Java es un proceso gestionado por otro. Es posible que desee diferenciar si ha finalizado con elegancia ( 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 larga ejecución sean periódicamente conscientes del estado interrumpido y lanzo una 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 la InterruptedException y se realiza una limpieza adecuada. Estos hilos están codificados para saber cómo responder a una solicitud de interrupción. Alto diseño de cohesion .

Entonces, en estos casos, agrego un enlace de apagado, que hace lo que creo que la JVM debería hacer de manera predeterminada: interrumpe todos los subprocesos no daemon 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<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"); } });

Complete la aplicación de prueba en github: https://github.com/idelvall/kill-test


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

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

Para poder manejar cualquier señal, debe salir de la JVM y entrar en el territorio del sistema operativo.

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

Luego me informan cuando sale la JVM, y por qué salió, y pueden tomar las medidas necesarias.


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 inconsistente.


La forma de manejar esto para cualquier cosa que no sea kill -9 sería registrar un gancho de shutdown . Si puede usar ( SIGTERM ) kill -15 el gancho de cierre funcionará. ( SIGINT ) kill -2 HACE que el programa salga con gracia y ejecute los ganchos de apagado.

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

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 se invoca el método de salida (equivalentemente, System.exit), o

* The virtual machine is terminated in response to a user

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

Intenté el siguiente programa de prueba en OSX 10.6.3 y en kill -9 NO ejecuté el gancho de cierre, no pensé que lo haría. En un kill -15 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 forma de manejar con gracia un kill -9 en ningún programa.

En circunstancias excepcionales, la máquina virtual puede abortar, es decir, dejar de funcionar sin apagarla 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 hacer que otro programa de observación mire si su programa principal se va o si usa un script de envoltura. Podría hacer esto con un script de shell que sondeó el comando ps buscando su programa en la lista y actuando en consecuencia cuando desapareció.

#!/usr/bin/env bash java TestShutdownHook wait # notify your other app that you quit echo "TestShutdownHook quit"


Puede usar Runtime.getRuntime().addShutdownHook(...) , pero no se le puede garantizar que será llamado en cualquier caso .