¿Cómo procesar la señal SIGTERM con gracia?

Resuelto zerkms asked hace 11 años • 10 respuestas

Supongamos que tenemos un demonio tan trivial escrito en Python:

def mainloop():
    while True:
        # 1. do
        # 2. some
        # 3. important
        # 4. job
        # 5. sleep

mainloop()

y lo demonizamos usando start-stop-daemonel cual por defecto envía la señal SIGTERM( TERM) en--stop .

Supongamos que el paso actual realizado es #2. Y en este mismo momento estamos enviandoTERM señal.

Lo que sucede es que la ejecución termina inmediatamente.

Descubrí que puedo manejar el evento de señal usando signal.signal(signal.SIGTERM, handler), pero el problema es que aún interrumpe la ejecución actual y pasa el control ahandler .

Entonces, mi pregunta es: ¿es posible no interrumpir la ejecución actual sino manejar la TERMseñal en un hilo separado (?) para poder configurarlo shutdown_flag = Truey mainloop()tener la oportunidad de detenerlo correctamente?

zerkms avatar Aug 29 '13 05:08 zerkms
Aceptado

Una solución limpia para usar basada en clases:

import signal
import time

class GracefulKiller:
  kill_now = False
  def __init__(self):
    signal.signal(signal.SIGINT, self.exit_gracefully)
    signal.signal(signal.SIGTERM, self.exit_gracefully)

  def exit_gracefully(self, signum, frame):
    self.kill_now = True

if __name__ == '__main__':
  killer = GracefulKiller()
  while not killer.kill_now:
    time.sleep(1)
    print("doing something in a loop ...")
   
  print("End of the program. I was killed gracefully :)")
Mayank Jaiswal avatar Jul 16 '2015 20:07 Mayank Jaiswal

Primero, no estoy seguro de que necesites un segundo hilo para configurar el archivo shutdown_flag.
¿Por qué no configurarlo directamente en el controlador SIGTERM?

Una alternativa es generar una excepción desde el SIGTERMcontrolador, que se propagará hacia la pila. Suponiendo que tenga un manejo de excepciones adecuado (por ejemplo, con bloques with/ contextmanagery try: ... finally:), este debería ser un cierre bastante elegante, similar a si estuviera en Ctrl+Csu programa.

Programa de ejemplo signals-test.py:

#!/usr/bin/python

from time import sleep
import signal
import sys


def sigterm_handler(_signo, _stack_frame):
    # Raises SystemExit(0):
    sys.exit(0)

if sys.argv[1] == "handle_signal":
    signal.signal(signal.SIGTERM, sigterm_handler)

try:
    print "Hello"
    i = 0
    while True:
        i += 1
        print "Iteration #%i" % i
        sleep(1)
finally:
    print "Goodbye"

Ahora mira el Ctrl+Ccomportamiento:

$ ./signals-test.py default
Hello
Iteration #1
Iteration #2
Iteration #3
Iteration #4
^CGoodbye
Traceback (most recent call last):
  File "./signals-test.py", line 21, in <module>
    sleep(1)
KeyboardInterrupt
$ echo $?
1

Esta vez lo envío SIGTERMdespués de 4 iteraciones con kill $(ps aux | grep signals-test | awk '/python/ {print $2}'):

$ ./signals-test.py default
Hello
Iteration #1
Iteration #2
Iteration #3
Iteration #4
Terminated
$ echo $?
143

Esta vez habilito mi SIGTERMcontrolador personalizado y lo envío SIGTERM:

$ ./signals-test.py handle_signal
Hello
Iteration #1
Iteration #2
Iteration #3
Iteration #4
Goodbye
$ echo $?
0
Will Manley avatar Jul 04 '2014 12:07 Will Manley