Python subprocess.communicate 在 parent 离开僵尸时挂起

Python subprocess.communicate hangs when parent leaves zombies

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

但是,当 A 本身生成更多子进程 BCD 且进程组不同时,这似乎不起作用 [=15= 】 那不肯死。即使在 A 已死并被标记为 defunct 之后,甚至在主进程使用 os.waitpid() 获得 A 以使其不再存在之后,线程仍拒绝与主线程连接。

只有children、BCD全部杀完,Popen.communicate才会return。

这种行为实际上是模块所期望的吗?递归等待在某些情况下可能很有用,但作为 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.

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

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

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

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

os.killpg(proc.pid,15)

而不是 proc.kill()。 B、C、D换组杀掉比较一般的问题是比较复杂的问题

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

Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate.

您卡在第 2 步:一直读到 end-of-file,因为睡眠使管道保持打开状态。