¿Cómo maneja la limpieza cuando el programa recibe una señal de interrupción?
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 finish
comando al cerrar la sesión. ¿Cuál es la mejor opción para enviar ese finish
comando cuando mi aplicación ha sido destruida con un 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 simplemente kill, que es lo mismo que ctrl-c
kill -9
significa para mí: "¡Vete, proceso inmundo, vete!", tras lo cual el proceso dejará de existir. Inmediatamente.kill
no es lo mismo que Ctrl-C, ya quekill
sin especificar qué señal enviar enviará SIGTERM, mientras que Ctrl-C envía SIGINT.Respuestas:
Es imposible para cualquier programa, en cualquier idioma, manejar un SIGKILL. Esto es para que siempre sea posible terminar 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. Los programas pueden manejar esa señal. El programa debe 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 manera de manejar esto para nada otra que
kill -9
sería registrar un apagado gancho. Si puede usar ( SIGTERM ),kill -15
el gancho de apagado funcionará. ( SIGINT )kill -2
HACE que el programa salga de manera elegante y ejecute los ganchos de cierre.He intentado lo siguiente programa de pruebas en OSX 10.6.3 y en el
kill -9
que lo hizo no correr el gancho de cierre, como se esperaba. En unkill -15
, SÍ ejecuta el gancho de cierre cada vez.No hay forma de manejar con gracia un
kill -9
en ningún programa.La única opción real para manejar un
kill -9
es hacer que otro programa de observación esté atento a que su programa principal desaparezca o usar un script de envoltura. Puede hacerlo con un script de shell que sondea elps
comando en busca de su programa en la lista y actúa en consecuencia cuando desaparece.fuente
No son formas de manejar sus propias señales en ciertos JVM - ver este artículo sobre el HotSpot JVM por ejemplo.
Al usar la
sun.misc.Signal.handle(Signal, SignalHandler)
llamada al método interno de Sun, también puede registrar un manejador de señales, pero probablemente no para señales comoINT
oTERM
como las usa la JVM.Para poder manejar cualquier señal, tendría que saltar de la JVM al 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 la JVM use la
waitpid
llamada al sistema.Luego se me informa cada vez que sale la JVM, y por qué salió, y puedo tomar las medidas necesarias.
fuente
INT
yTERM
consun.misc.Signal
, pero no puede manejarloQUIT
porque la JVM lo reserva para la depuración, niKILL
porque el sistema operativo terminará la JVM inmediatamente. Intentar manejar cualquiera de ellos generará unIllegalArgumentException
.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ñalesSIGINT (kill -2)
ySIGTERM (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 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 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
del proceso del administrador) o si se ha producido un cierre (para reiniciar automáticamente el trabajo al inicio).Como base, siempre hago que mis hilos de larga duración sean conscientes periódicamente del estado interrumpido y lanzo un mensaje
InterruptedException
si se 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 manera predeterminada: interrumpir todos los subprocesos que no son demonios creados por mi aplicación que aún se están ejecutando:
Solicitud de prueba completa en github: https://github.com/idelvall/kill-test
fuente
Puede usar
Runtime.getRuntime().addShutdownHook(...)
, pero no se le puede garantizar que se llamará en cualquier caso .fuente
Hay una forma de reaccionar ante un kill -9: es tener un proceso separado que supervise el proceso que se está matando y lo limpie si es necesario. Esto probablemente involucraría IPC y sería bastante trabajo, y aún puede anularlo eliminando 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 lo que está haciendo y que puede dejar las cosas en un estado inconsistente.
fuente