如何使用subprocess模块终止(或避免)僵尸进程

60 投票
11 回答
75806 浏览
提问于 2025-04-15 22:18

当我在一个Python脚本里启动另一个Python脚本时,使用了一个叫做subprocess的模块。这个时候会出现一个“僵尸进程”,当子进程完成后就会变成这样。我无法单独结束这个子进程,除非我把父脚本也结束掉。

有没有办法在不结束父脚本的情况下,单独结束这个子进程呢?我知道可以用wait()来做到这一点,但我需要用no_wait()来运行我的脚本。

11 个回答

17

如果你用del命令删除一个子进程对象,这样会强制进行垃圾回收。结果就是这个子进程对象会被删除,然后那些无用的进程也会消失,而不会影响到你的解释器。你可以先在Python的命令行界面试试看。

23

如果不使用 Popen.communicate()call(),就会出现僵尸进程。

如果你不需要命令的输出,可以使用 subprocess.call()

>>> import subprocess
>>> subprocess.call(['grep', 'jdoe', '/etc/passwd'])
0

如果输出很重要,你应该使用 Popen()communicate() 来获取标准输出和错误输出。

>>> from subprocess import Popen, PIPE
>>> process = Popen(['ls', '-l', '/tmp'], stdout=PIPE, stderr=PIPE)
>>> stdout, stderr = process.communicate()
>>> stderr
''
>>> print stdout
total 0
-rw-r--r-- 1 jdoe jdoe 0 2010-05-03 17:05 bar
-rw-r--r-- 1 jdoe jdoe 0 2010-05-03 17:05 baz
-rw-r--r-- 1 jdoe jdoe 0 2010-05-03 17:05 foo
31

僵尸进程并不是真正的进程;它只是进程表中的一个残留条目,直到父进程请求子进程的返回代码。实际上,那个进程已经结束,不再需要其他资源,只需要在进程表中的这个条目。

为了更好地帮助你,我们可能需要更多关于你运行的进程的信息。

不过,如果你的Python程序知道子进程什么时候结束(比如通过读取完子进程的输出数据),那么你可以安全地调用process.wait()

import subprocess

process= subprocess.Popen( ('ls', '-l', '/tmp'), stdout=subprocess.PIPE)

for line in process.stdout:
        pass

subprocess.call( ('ps', '-l') )
process.wait()
print("after wait")
subprocess.call( ('ps', '-l') )

示例输出:

$ python so2760652.py
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S   501 21328 21326  0  80   0 -  1574 wait   pts/2    00:00:00 bash
0 S   501 21516 21328  0  80   0 -  1434 wait   pts/2    00:00:00 python
0 Z   501 21517 21516  0  80   0 -     0 exit   pts/2    00:00:00 ls <defunct>
0 R   501 21518 21516  0  80   0 -   608 -      pts/2    00:00:00 ps
after wait
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S   501 21328 21326  0  80   0 -  1574 wait   pts/2    00:00:00 bash
0 S   501 21516 21328  0  80   0 -  1467 wait   pts/2    00:00:00 python
0 R   501 21519 21516  0  80   0 -   608 -      pts/2    00:00:00 ps

否则,你可以把所有子进程放在一个列表里,然后不时地用.poll来检查它们的返回代码。在每次检查后,记得从列表中移除返回代码不是None(也就是已经结束的进程)的子进程。

撰写回答