我遇到一些使用监视程序进程运行的python进程的奇怪问题。
监视程序进程是用python编写的并且是父进程,并且有一个名为 start_child(name)的函数,它使用 subprocess.Popen 来打开子进程。记录Popen对象,以便监视程序可以使用 poll()监视进程,并在需要时最终以 terminate()结束。 如果孩子意外死亡,看门狗再次调用 start_child(name)并记录新的Popen对象。
有7个子进程,所有这些进程也都是python。如果我手动运行任何子项,我可以使用 kill 发送SIGTERM或SIGINT并获得我期望的结果(过程结束)。
然而,当从看门狗进程运行时,子进程将仅在 FIRST 信号之后结束。当监视程序重新启动子进程时,新的子进程不再响应SIGTERM或SIGINT。我不知道造成这种情况的原因。
watchdog.py
class watchdog:
# <snip> various init stuff
def start(self):
self.running = true
kids = ['app1', 'app2', 'app3', 'app4', 'app5', 'app6', 'app7']
self.processes = {}
for kid in kids:
self.start_child(kid)
self.thread = threading.Thread(target=self._monitor)
self.thread.start()
while self.running:
time.sleep(10)
def start_child(self, name):
try:
proc = subprocess.Popen(name)
self.processes[name] = proc
except:
print "oh no"
else:
print "started child ok"
def _monitor(self):
while self.running:
time.sleep(1)
if self.running:
for kid, proc in self.processes.iteritems():
if proc.poll() is not None: # process ended
self.start_child(kid)
所以会发生什么是 watchdog.start()启动所有7个进程,如果我发送任何进程SIGTERM,它结束,监视器线程再次启动它。但是,如果我然后发送新进程SIGTERM,它会忽略它。
我应该能够一遍又一遍地向重启的进程发送kill -15。为什么他们在重新启动后会忽略它?
答案 0 :(得分:5)
如下所述:http://blogs.gentoo.org/agaffney/2005/03/18/python_sucks,当Python创建一个新线程时,它会阻止该线程的所有信号(以及线程生成的任何进程)。
我使用sigprocmask修复此问题,通过ctypes调用。这可能是也可能不是“正确”的方式,但确实有效。
在子进程中,__init__
期间:
libc = ctypes.cdll.LoadLibrary("libc.so")
mask = '\x00' * 17 # 16 byte empty mask + null terminator
libc.sigprocmask(3, mask, None) # '3' on FreeBSD is the value for SIG_SETMASK
答案 1 :(得分:0)
在Python中恢复默认信号处理程序而不是通过ctypes会不会更好?在您的子进程中,使用信号模块:
import signal
for sig in range(1, signal.NSIG):
try:
signal.signal(sig, signal.SIG_DFL)
except RuntimeError:
pass
尝试设置无法捕获的SIGKILL等信号时会引发RuntimeError。