如何杀死(或避免)与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进行通信,就调用它。