子进程失效,`communicate()` 挂起

Posted

技术标签:

【中文标题】子进程失效,`communicate()` 挂起【英文标题】:subprocess becomes defunct, `communicate()` hangs 【发布时间】:2018-11-11 18:31:49 【问题描述】:

在 Ubuntu 14.04 上的 python 2.7 中,我启动了一个这样的进程:

bag_process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
for i in range(5):
    print "Countdown: ".format(5 - i - 1)
    time.sleep(1)
print "Sending SIGINT to PID ".format(bag_process.pid)
bag_process.send_signal(signal.SIGINT)
(bag_out, bag_err) = bag_process.communicate()

程序挂在communicate() 行。当我打开另一个终端时,我运行ps -ef | grep ### 来查找子进程的pid,我看到它是<defunct>

为什么子程序失效,而父程序挂在communicate()?如果子进程在收到SIGINT 后真正退出,我怎样才能让父程序可靠地处理它而不挂起?

【问题讨论】:

它已经失效了,因为它已经退出了,但是父进程还没有读出退出代码,见en.wikipedia.org/wiki/Zombie_process。也许 SIGINT 对子进程所附加的管道做了一些事情,比如发出重新打开文件句柄的信号?如果没有流程本身,这不是我们可以更具体的。 您的死程序是否有可能启动仍然活着的子程序(它们从未关闭它们继承的 stdout 和 stderr),或者通过套接字将其 stdout 或 stderr FD 传递给仍然打开它们的其他程序?这两种情况都可以为communicate() 无法完成和返回提供可靠的解释。 @MartijnPieters 谢谢,但我相信communicate() 方法使用wait() 来获取退出状态,即使communicate() 本身不返回退出状态。 github.com/python/cpython/blob/2.7/Lib/subprocess.py#L452 @CharlesDuffy 好主意,但没有骰子...我为已失效的 pid 进行了搜索,它没有被列为任何其他 PID 的 PPID。 @EdwardNedHarvey:是的,但它永远不会到达那里,因为管道被某处阻塞。 【参考方案1】:

问题是:不要像这样杀死进程:

bag_process.send_signal(signal.SIGINT)

相反,像这样终止进程及其所有子进程:

parent = psutil.Process(bag_process.pid)
for child in parent.get_children(recursive=True):
    child.send_signal(signal.SIGINT)
bag_process.send_signal(signal.SIGINT)

【讨论】:

以上是关于子进程失效,`communicate()` 挂起的主要内容,如果未能解决你的问题,请参考以下文章

为啥在不同线程中调用 asyncio subprocess.communicate 会挂起?

如何使用子进程 Popen.communicate() 方法?

我啥时候应该在子进程中使用`wait`而不是`communicate`?

Linux中线程的挂起与恢复(进程暂停)

当 Popen.communicate() 还不够?

python子进程模块subprocess详解