当父离开僵尸时,Python subprocess.communicate会挂起

问题描述 投票:3回答:1

我正在尝试使用Popen创建子进程A以及使用Popen.communicate与之通信的线程。主进程将使用具有指定超时的Thread.join在线程上等待,并在超时到期后终止A,这将导致线程也死亡。

然而,当A本身产生更多的子过程BCD与不同的过程组而不是A拒绝死亡时,这似乎不起作用。即使在A已经死亡并标记为已解散之后,甚至在主要进程使用A使os.waitpid()不再存在之后,该线程拒绝加入主线程。

只有在所有的孩子,BCD被杀后,Popen.communicate终于回来了。

这个行为实际上是从模块中预期的吗?在某些情况下,递归等待可能很有用,但它肯定不适合作为Popen.communicate的默认行为。如果这是预期的行为,有没有办法覆盖它?

这是一个非常简单的例子:

from subprocess import PIPE, Popen
from threading import Thread
import os
import time
import signal

DEVNULL = open(os.devnull, 'w')

proc = Popen(["/bin/bash"], stdin=PIPE, stdout=PIPE,
             stderr=DEVNULL, start_new_session=True)


def thread_function():
    print("Entering thread")
    return proc.communicate(input=b"nohup sleep 100 &\nexit\n")


thread = Thread(target=thread_function)
thread.start()
time.sleep(1)
proc.kill()
while True:
    thread.join(timeout=5)
    if not thread.is_alive():
        break
    print("Thread still alive")

这是在Linux上。

python multithreading popen python-multiprocessing python-multithreading
1个回答
1
投票

我认为这来自于在Linux中编写popen.communicate方法的一种相当自然的方式。 Proc.communicate()似乎读取stdin文件描述符,当进程终止时它将返回EOF。然后它等待获取进程的退出代码。

在您的示例中,sleep进程从bash进程继承stdin文件描述符。因此,当bash进程终止时,popen.communicate没有在stdin管道上获得EOF,因为睡眠仍然打开它。解决此问题的最简单方法是将通信线路更改为:

return proc.communicate(input=b"nohup sleep 100 >/dev/null&\nexit\n")

这会导致你的线程在bash死亡后立即结束...由于退出,而不是你的proc.kill,在这种情况下。但是,如果使用exit语句或proc.kill调用,则在bash死后,sleep仍在运行。如果你想杀死睡眠,我会用

os.killpg(proc.pid,15)

而不是proc.kill()。杀死B,C和D的更一般的问题是,如果他们改变组是一个更复杂的问题。

附加数据:我找不到这种proc.communicate方法的官方文档,但我忘记了最明显的地方:-)我在this answer的帮助下找到了它。 docs for communicate说:

与流程交互:将数据发送到stdin。从stdout和stderr读取数据,直到达到文件结尾。等待进程终止。

您在步骤2中遇到困难:读取直到文件结束,因为睡眠会使管道保持打开状态。

© www.soinside.com 2019 - 2024. All rights reserved.