如何正确处理SIGTERM信号?
假设我们有这样一个用python编写的简单的守护进程:
def mainloop(): while True: # 1. do # 2. some # 3. important # 4. job # 5. sleep mainloop()
我们使用start-stop-daemon
,默认情况下,它会在-stop上发送SIGTERM
( TERM
)信号。
假设当前执行的步骤是#2
。 在这个时刻,我们发送TERM
信号。
发生什么事是执行立即终止。
我发现我可以使用signal.signal(signal.SIGTERM, handler)
处理信号事件signal.signal(signal.SIGTERM, handler)
但是它仍然会中断当前的执行并将控制权交给handler
。
所以,我的问题是 – 是否有可能不中断当前的执行,但在单独的线程(?)处理TERM
信号,以便我能够设置shutdown_flag = True
以便mainloop()
有机会优雅地停止?
基于类的清洁使用解决scheme:
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 True: time.sleep(1) print("doing something in a loop ...") if killer.kill_now: break print "End of the program. I was killed gracefully :)"
我认为你接近一个可能的解决scheme。
在一个单独的线程中执行mainloop
,并使用属性shutdown_flag
对其进行扩展。 signal.signal(signal.SIGTERM, handler)
在主线程中捕获(不在单独的线程中)。 信号处理程序应该将shutdown_flag
设置为True,并等待线程以thread.join()
结束
首先我不确定你需要第二个线程来设置shutdown_flag。 为什么不直接在SIGTERM处理程序中设置?
另一种方法是从SIGTERM
处理程序中引发一个exception,并将其传播到堆栈中。 假设你已经得到了适当的exception处理(例如with with
contextmanager
和try: ... finally:
blocks),这应该是一个相当优美的closures,类似于如果你要Ctrl-C
你的程序。
示例程序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"
现在看到Ctrl-C的行为:
$ ./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
这次我用kill $(ps aux | grep signals-test | awk '/python/ {print $2}')
在4次迭代后发送SIGTERM
:
$ ./signals-test.py default Hello Iteration #1 Iteration #2 Iteration #3 Iteration #4 Terminated $ echo $? 143
这一次,我启用我的自定义SIGTERM
处理程序,并发送SIGTERM
:
$ ./signals-test.py handle_signal Hello Iteration #1 Iteration #2 Iteration #3 Iteration #4 Goodbye $ echo $? 0
这是一个没有线程或类的简单例子。
import signal run = True def handler_stop_signals(signum, frame): global run run = False signal.signal(signal.SIGINT, handler_stop_signals) signal.signal(signal.SIGTERM, handler_stop_signals) while run: pass # do stuff including other IO stuff