我试图为我的python守护程序进程创建类似supervisor的东西,并发现相同的代码在python2中工作,并且不能在python3中工作。
一般来说,我来到这个最小的示例代码。
daemon.py
#!/usr/bin/env python
import signal
import sys
import os
def stop(*args, **kwargs):
print('daemon exited', os.getpid())
sys.exit(0)
signal.signal(signal.SIGTERM, stop)
print('daemon started', os.getpid())
while True:
pass
supervisor.py
import os
import signal
import subprocess
from time import sleep
parent_pid = os.getpid()
commands = [
[
'./daemon.py'
]
]
popen_list = []
for command in commands:
popen = subprocess.Popen(command, preexec_fn=os.setsid)
popen_list.append(popen)
def stop_workers(*args, **kwargs):
for popen in popen_list:
print('send_signal', popen.pid)
popen.send_signal(signal.SIGTERM)
while True:
popen_return_code = popen.poll()
if popen_return_code is not None:
break
sleep(5)
signal.signal(signal.SIGTERM, stop_workers)
for popen in popen_list:
print('wait_main', popen.wait())
如果你运行supervisor.py然后在它的pid上调用kill -15
,那么它将挂起无限循环,因为popen_return_code永远不会是None。我发现,它主要是因为为wait_pid操作添加了threading.Lock(source),但是如何重写代码以便它能正确处理子退出?
答案 0 :(得分:2)
这是一个有趣的案例。
我花了几个小时试图找出发生这种情况的原因,此时我唯一想到的是wait()
和poll()
的实施已经改变在python3
与python2.7
。
查看python3/suprocess.py
实现的源代码,我们可以看到当您调用wait()
对象的Popen
方法时会发生锁获取,请参阅
https://github.com/python/cpython/blob/master/Lib/subprocess.py#L1402
此锁定会阻止进一步的poll()
调用按预期工作,直到wait()
获取的锁定将被释放,请参阅
https://github.com/python/cpython/blob/master/Lib/subprocess.py#L1355
并在那里发表评论
还有其他东西在忙着调用waitpid。不要两个 立刻。我们什么都不知道。
python2.7/subprocess.py
中没有这样的锁定,所以这看起来就像python2.7
一样,并且在python3
中无效。
但是,我没有看到您在信号处理程序中尝试poll()
的原因,尝试重写您的supervisor.py
如下,这应该在{{1}上按预期工作}和python3
<强> supervisor.py 强>
python2.7
希望这有帮助
答案 1 :(得分:1)
一般来说,我同意@ risboo6909的回答,但也有一些想法,如何解决这种情况。
subproccess.Popen
更改为psutil.Popen
。popen.wait()
中,您可以执行无限循环,因为进程将在信号处理程序中退出。