如何杀死(或避免)与subprocess模块的僵尸进程
当我使用subprocess模块在另一个python脚本中启动python脚本时,当subprocess“完成”时会创build一个僵尸进程。 我无法杀死这个subprocess,除非我杀了我的父Python进程。
有没有办法杀死subprocess没有杀死父母? 我知道我可以通过使用wait()来做到这一点,但我需要用no_wait()来运行我的脚本。
不使用Popen.communicate()
或call()
将导致僵尸进程。
如果你不需要命令的输出,你可以使用subprocess.call()
:
>>> import subprocess >>> subprocess.call(['grep', 'jdoe', '/etc/passwd']) 0
如果输出是重要的,你应该使用Popen()
和communicate()
来获得stdout和stderr。
>>> 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
僵尸进程不是一个真正的进程, 只是在父进程请求subprocess的返回码之前,这只是进程表中的一个剩余条目。 实际的过程已经结束,并且不需要其他的资源,但是所述的过程表条目。
我们可能需要更多关于您运行的stream程的信息才能真正帮助更多。
但是,如果您的Python程序知道subprocess何时结束(例如,达到子标准输出数据的末尾),那么您可以安全地调用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 FS 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 FS 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
(即完成的)的子项。
如果删除subprocess对象,使用del强制垃圾回收,这将导致subprocess对象被删除,然后停止进程将不会终止您的解释器。 你可以先在python命令行界面中试试。
python的运行时间负责摆脱僵尸进程,一旦他们的进程对象被垃圾收集。 如果你看到僵尸躺在它周围意味着你已经保持一个进程对象,而不是叫等待,轮询或终止。
如果你只是使用subprocess.Popen
,你会没事的 – 这是如何:
import subprocess def spawn_some_children(): subprocess.Popen(["sleep", "3"]) subprocess.Popen(["sleep", "3"]) subprocess.Popen(["sleep", "3"]) def do_some_stuff(): spawn_some_children() # do some stuff print "children went out to play, now I can do my job..." # do more stuff if __name__ == '__main__': do_some_stuff()
您可以在.poll()
返回的对象上使用.poll()
来检查是否完成(不等待)。 如果返回None
,孩子仍在跑步。
确保你不要保留对Popen对象的引用 – 如果你这样做,他们将不会被垃圾收集,所以你最终与僵尸。 这是一个例子:
import subprocess def spawn_some_children(): children = [] children.append(subprocess.Popen(["sleep", "3"])) children.append(subprocess.Popen(["sleep", "3"])) children.append(subprocess.Popen(["sleep", "3"])) return children def do_some_stuff(): children = spawn_some_children() # do some stuff print "children went out to play, now I can do my job..." # do more stuff # if children finish while we are in this function, # they will become zombies - because we keep a reference to them
在上面的例子中,如果你想摆脱僵尸,你可以.wait()
每个孩子或.poll()
直到结果不是None
。
无论哪种方式是好的 – 要么不保留引用,要么使用.wait()
或.poll()
。
我不知道你的意思是“我需要用no_wait()来运行我的脚本”,但是我认为这个例子可以满足你的需求。 进程不会僵尸很长时间。 父进程只会在它们实际已经被终止的时候wait()
,因此它们会很快地被解除。
#!/usr/bin/env python2.6 import subprocess import sys import time children = [] #Step 1: Launch all the children asynchronously for i in range(10): #For testing, launch a subshell that will sleep various times popen = subprocess.Popen(["/bin/sh", "-c", "sleep %s" % (i + 8)]) children.append(popen) print "launched subprocess PID %s" % popen.pid #reverse the list just to prove we wait on children in the order they finish, #not necessarily the order they start children.reverse() #Step 2: loop until all children are terminated while children: #Step 3: poll all active children in order children[:] = [child for child in children if child.poll() is None] print "Still running: %s" % [popen.pid for popen in children] time.sleep(1) print "All children terminated"
最后的输出如下所示:
Still running: [29776, 29774, 29772] Still running: [29776, 29774] Still running: [29776] Still running: [] All children terminated
我不完全确定你的意思是no_wait()
。 你的意思是你不能阻止等待subprocess完成? 假设如此,我认为这将做你想要的:
os.wait3(os.WNOHANG)
最近,由于我的Python脚本,我遇到了这个僵尸问题。 实际的问题主要是由于subprocess的死亡,父进程不知道孩子死了。 所以我所做的只是在subprocess的kill信号之后添加popen.communicate(),以便父进程知道subprocess已经死了,然后内核更新subprocess的PID,因为subprocess没有了,所以现在没有僵尸了
PS:民意调查也是一种select,因为它会检查并向父母传达有关儿童状况的信息。 通常在subprocess中,最好是使用check_output或者如果你不需要和stdout和stdin进行通信,就调用它。