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

时间:2018-02-12 09:38:51

标签: python multithreading popen python-multiprocessing python-multithreading

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

但是,当A本身产生的子进程BCDA不同的进程组时,这似乎不起作用拒绝死即使A已经死亡并标记为已失效,即使主进程使用A重新使用os.waitpid()以使其不再存在,该线程也拒绝与主线程连接。 / p>

只有在所有孩子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上。

1 个答案:

答案 0 :(得分: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读取数据,直到达到文件结尾。等待进程终止。

你在第二步陷入困境:阅读直到文件结束,因为睡眠会使管道保持打开状态。