如何正确处理SIGTERM信号?

假设我们有这样一个用python编写的简单的守护进程:

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

我们使用start-stop-daemon ,默认情况下,它会在-stop上发送SIGTERMTERM )信号。

假设当前执行的步骤是#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 contextmanagertry: ... 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