无法在python3中获取子进程返回代码

我试图为我的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())

希望这可以帮助

上一篇:Python执行脚本方法


下一篇:python-完全独立于脚本运行命令