zer*_*kms 177 python daemon sigterm start-stop-daemon
让我们假设我们有一个用python编写的这样一个简单的守护进程:
def mainloop():
while True:
# 1. do
# 2. some
# 3. important
# 4. job
# 5. sleep
mainloop()
Run Code Online (Sandbox Code Playgroud)
然后我们使用start-stop-daemon
默认的send SIGTERM
(TERM
)信号进行守护--stop
.
我们假设当前执行的步骤是#2
.在这个时刻,我们正在发送TERM
信号.
会发生什么是执行立即终止.
我发现我可以处理信号事件,signal.signal(signal.SIGTERM, handler)
但问题是它仍然会中断当前执行并将控制传递给handler
.
所以,我的问题是-它可以不中断当前执行,但处理TERM
(?)在独立的线程信号,使我能够设置shutdown_flag = True
,使mainloop()
有机会停止正常?
May*_*wal 241
基于类的清洁使用解决方案:
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 :)"
Run Code Online (Sandbox Code Playgroud)
Wil*_*ley 48
首先,我不确定您是否需要第二个线程来设置shutdown_flag.为什么不直接在SIGTERM处理程序中设置它?
另一种方法是从SIGTERM
处理程序引发异常,该异常将在堆栈中向上传播.假设你有适当的异常处理(例如使用with
/ contextmanager
和try: ... finally:
块),这应该是一个相当优雅的关闭,类似于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"
Run Code Online (Sandbox Code Playgroud)
现在看看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
Run Code Online (Sandbox Code Playgroud)
这次我SIGTERM
在4次迭代后发送它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
Run Code Online (Sandbox Code Playgroud)
这次我启用自定义SIGTERM
处理程序并发送它SIGTERM
:
$ ./signals-test.py handle_signal
Hello
Iteration #1
Iteration #2
Iteration #3
Iteration #4
Goodbye
$ echo $?
0
Run Code Online (Sandbox Code Playgroud)
mol*_*are 28
我认为你接近可能的解决方案.
mainloop
在单独的线程中执行并使用属性扩展它shutdown_flag
.信号可以signal.signal(signal.SIGTERM, handler)
在主线程中捕获(而不是在单独的线程中).信号处理程序应设置shutdown_flag
为True并等待线程结束thread.join()
小智 24
这是一个没有线程或类的简单示例.
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
Run Code Online (Sandbox Code Playgroud)
Okk*_*kke 11
基于之前的答案,我创建了一个上下文管理器,可以防止sigint和sigterm.
import logging
import signal
import sys
class TerminateProtected:
""" Protect a piece of code from being killed by SIGINT or SIGTERM.
It can still be killed by a force kill.
Example:
with TerminateProtected():
run_func_1()
run_func_2()
Both functions will be executed even if a sigterm or sigkill has been received.
"""
killed = False
def _handler(self, signum, frame):
logging.error("Received SIGINT or SIGTERM! Finishing this block, then exiting.")
self.killed = True
def __enter__(self):
self.old_sigint = signal.signal(signal.SIGINT, self._handler)
self.old_sigterm = signal.signal(signal.SIGTERM, self._handler)
def __exit__(self, type, value, traceback):
if self.killed:
sys.exit(0)
signal.signal(signal.SIGINT, self.old_sigint)
signal.signal(signal.SIGTERM, self.old_sigterm)
if __name__ == '__main__':
print("Try pressing ctrl+c while the sleep is running!")
from time import sleep
with TerminateProtected():
sleep(10)
print("Finished anyway!")
print("This only prints if there was no sigint or sigterm")
Run Code Online (Sandbox Code Playgroud)
为我找到了最简单的方法。这里有一个使用 fork 的例子,为了清楚起见,这种方式对于流量控制很有用。
import signal
import time
import sys
import os
def handle_exit(sig, frame):
raise(SystemExit)
def main():
time.sleep(120)
signal.signal(signal.SIGTERM, handle_exit)
p = os.fork()
if p == 0:
main()
os._exit()
try:
os.waitpid(p, 0)
except (KeyboardInterrupt, SystemExit):
print('exit handled')
os.kill(p, signal.SIGTERM)
os.waitpid(p, 0)
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
133619 次 |
最近记录: |