我试图为我的python守护进程创建类似超级用户的东西,发现相同的代码在python2中有效,而在python3中不起作用.
通常,我来介绍这个最小的示例代码.
守护进程
#!/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
主管
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.我发现,这基本上是因为添加了threading.Lock用于wait_pid操作(source),但是如何重写代码以使其能够正确处理子退出?
解决方法:
这是一个有趣的案例.
我花了几个小时试图弄清楚为什么会发生这种情况,而我现在想到的唯一一件事就是,在python3和python2.7中已经更改了wait()和poll()的实现.
查看python3 / suprocess.py实现的源代码,我们可以看到调用Popen对象的wait()方法时发生了锁获取,请参见
https://github.com/python/cpython/blob/master/Lib/subprocess.py#L1402.
此锁可防止进一步的poll()调用按预期工作,直到由wait()获得的锁被释放为止,请参见
https://github.com/python/cpython/blob/master/Lib/subprocess.py#L1355
并在那里发表评论
Something else is busy calling waitpid. Don’t allow two
at once. We know nothing yet.
python2.7 / subprocess.py中没有这样的锁,因此这看起来像是它在python2.7中起作用而在python3中不起作用的原因.
但是我没有看到为什么要尝试在信号处理程序中进行poll()的原因,请尝试按以下方式重写您的supervisor.py,这应该在python3和python2.7上都能正常工作
主管
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)
signal.signal(signal.SIGTERM, stop_workers)
for popen in popen_list:
print('wait_main', popen.wait())
希望这可以帮助