无法在python3

时间:2016-12-14 05:43:04

标签: python python-3.x subprocess

我试图为我的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),但是如何重写代码以便它能正确处理子退出?

2 个答案:

答案 0 :(得分:2)

这是一个有趣的案例。

我花了几个小时试图找出发生这种情况的原因,此时我唯一想到的是wait()poll()的实施已经改变在python3python2.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的回答,但也有一些想法,如何解决这种情况。

  1. 您可以将subproccess.Popen更改为psutil.Popen
  2. 在主循环而不是popen.wait()中,您可以执行无限循环,因为进程将在信号处理程序中退出。