[英]how to kill (or avoid) zombie processes with subprocess module
當我使用子進程模塊從另一個 python 腳本中啟動一個 python 腳本時,當子進程“完成”時會創建一個僵屍進程。 除非我殺死我的父 python 進程,否則我無法殺死這個子進程。
有沒有辦法在不殺死父進程的情況下殺死子進程? 我知道我可以使用 wait() 來做到這一點,但我需要使用 no_wait() 來運行我的腳本。
僵屍進程不是真正的進程; 它只是進程表中的剩余條目,直到父進程請求子進程的返回代碼。 實際進程已經結束,除了所述進程表條目外不需要其他資源。
我們可能需要更多有關您運行的流程的信息才能真正提供更多幫助。
但是,如果您的 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
(即完成的子項)。
不使用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
如果您刪除子進程對象,使用del強制垃圾收集,這將導致子進程對象被刪除,然后失效的進程將消失而不終止您的解釋器。 您可以先在 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()
。
一旦進程對象被垃圾回收,python 運行時負責清除僵屍進程。 如果您看到僵屍躺在它周圍,則意味着您保留了一個進程對象,並且沒有在其上調用等待、輪詢或終止。
我不確定你的意思是“我需要用 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
像這樣:
s = Popen(args)
s.terminate()
time.sleep(0.5)
s.poll()
有用
僵屍進程將消失
我不完全確定您所說的no_wait()
是什么意思。 你的意思是你不能阻止等待子進程完成? 假設是這樣,我認為這會做你想要的:
os.wait3(os.WNOHANG)
最近,由於我的python腳本,我遇到了這個僵屍問題。 實際問題主要是由於子進程被殺死而父進程不知道子進程已死。 所以我所做的是,只是在子進程的終止信號之后添加 popen.communicate() 以便父進程知道子進程已經死了,然后內核更新子進程的pid,因為子進程不再存在並且所以現在沒有僵屍形成。
PS:poll 在這里也是一個選項,因為它會檢查子狀態並將其傳達給父級。 通常在子進程中,如果您不需要與 stdout 和 stdin 通信,則最好使用 check_output 或調用。
當您不需要等待您生成的任何子進程時,防止僵屍進程的最簡單解決方案是調用signal(SIGCHLD, SIG_IGN);
在初始化期間。 然后,立即刪除終止的子進程。 此設置適用於整個過程,因此您只能在沒有需要等待的孩子時使用它。
在 Python:
import signal
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
# …
# call subprocess.Popen(…) as needed
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.