5

我们正在使用 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

该进程尚未被杀死/失效。使用subprocess.call()带有'kill'pid作为参数的函数将发出 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 中处理已失效的进程?

有没有更好的杀死进程的方法?

4

2 回答 2

19

这里有两个主要问题:

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

在您的情况下,您使用sleep的是不是内置的,因此您可以 drop shell=True,并Popen会产生实际的进程 id:p.terminate()会起作用。

大多数情况下,您可以(并且应该)避免shell=True,即使它需要额外的 python 编码工作(将 2 个命令连接在一起,重定向输入/输出,所有这些情况都可以由一个或多个Popen 没有 shell=True.

并且(第二个问题)如果在该修复后终止时进程仍然失效,您可以调用p.wait()(来自这个问题)。看来打电话terminate是不够的。该Popen对象需要被垃圾收集。

于 2017-01-31T15:36:23.023 回答
6

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

于 2017-01-31T16:21:45.047 回答