如何干净地杀死 python 中的子进程

How to cleanly kill subprocesses in python

我们正在使用 python 流程来管理较长的 运行 python 子流程。有时需要终止子进程。 kill 命令不会完全杀死进程,只会使其失效。

运行 以下脚本演示了此行为。

import subprocess
p = subprocess.Popen(['sleep', '400'], stdout=subprocess.PIPE, shell=False)

p = subprocess.Popen('sleep 400', stdout=subprocess.PIPE, shell=True)

将创建一个子进程。

p.terminate() 
p.kill()

对进程没有任何作用。 ps aux | grep sleep

展示
$ ps aux| grep 'sleep'
User       8062  0.0  0.0   7292   764 pts/7    S    14:53   0:00 sleep 400

该进程尚未 killed/made 失效。使用带有 'kill'pid 作为参数的 subprocess.call() 函数将发出 kill 命令。

subprocess.call(['kill', str(p.pid)])

这将终止该进程,但它现在已经不存在了。

$ ps aux | grep 'sleep'
User       8062  0.0  0.0      0     0 pts/7    Z+   14:51   0:00 [sleep] <defunct>

如果队列 运行 足够长,它最终会达到其最大进程数,还是最终会回收已停用的进程并且没问题?

如果答案是前者,我如何在不杀死父进程的情况下处理 python 中的已失效进程?

是否有更好的终止进程的方法?

这里有两个主要问题:

第一个问题:如果您正在使用 shell=True,那么您将终止 shell 运行 进程,而不是过程本身。父进程被杀死后,子进程就会失效/不会立即被杀死。

在您的情况下,您使用的是 sleep 而不是 built-in,因此您可以删除 shell=True,而 Popen 将产生实际的进程 ID: p.terminate() 可以。

大多数时候你可以(而且你应该)避免 shell=True,即使它需要额外的 python 编码工作(将 2 个命令连接在一起,重定向 input/output,所有这些案件可以由一个或几个人很好地处理 Popen without shell=True.

并且(第二个问题)如果在该修复后终止时进程仍然不存在,您可以调用 p.wait()(来自 this 问题)。似乎调用 terminate 是不够的。 Popen 对象需要进行垃圾回收。

您应该在终止子进程后调用 p.wait() - 用于清除进程 table。那应该删除僵尸进程(处于 defunct 状态)