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

无法在python3中获取子进程返回代码,python,python-3.x,subprocess,Python,Python 3.x,Subprocess,我正在尝试为我的python守护进程创建类似supervisor的东西,发现同样的代码在python2中工作,在python3中不工作 一般来说,我已经得到了这个最小的示例代码 守护进程.py #!/usr/bin/env python import signal import sys import os def stop(*args, **kwargs): print('daemon exited', os.getpid()) sys.exit(0) signal.s

我正在尝试为我的python守护进程创建类似supervisor的东西,发现同样的代码在python2中工作,在python3中不工作

一般来说,我已经得到了这个最小的示例代码

守护进程.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
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())
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())
supervisor.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
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())
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())

如果运行supervisor.py,然后在其pid上调用
kill-15
,那么它将挂起在无限循环中,因为popen\u return\u code永远不会是None。我发现,这基本上是因为添加了threading.Lock for wait_pid operation(),但是我如何重写代码以便正确处理子退出?

这是一个有趣的案例

我花了几个小时试图找出发生这种情况的原因,目前我唯一想到的是,
wait()
poll()
的实现在
python3
python2.7
中发生了变化

查看
python3/supprocess.py
实现的源代码,我们可以看到在调用
Popen
对象的
wait()
方法时发生锁获取,请参见

此锁阻止进一步的
poll()
调用按预期工作,直到
wait()
获取的锁被释放,请参阅

并在那里发表评论

有人正忙着给waitpid打电话。不允许两个 马上。我们什么都不知道

python2.7/subprocess.py
中没有这样一个锁,因此这似乎是它在
python2.7
中工作而在
python3
中不工作的原因

但是,我看不出您为什么要在信号处理程序中尝试
poll()
,请尝试按如下方式重写您的
supervisor.py
,这应该在
python3
python2.7
上都能正常工作

supervisor.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
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())
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())

希望这有帮助

一般来说,我同意@risboo6909的答案,但也有一些想法,如何解决这种情况

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

  • 如果我不处理child exist并只发送SIGTERM,那么当我的孩子没有实现handler(尝试从
    daemon.py
    中删除
    sys.exit
    )时,它将被分离到init进程。对于popen\u列表中的popen:print('wait\u main',popen.wait()),很抱歉,这里有混乱的评论:(