簡體   English   中英

如何使用子進程模塊殺死(或避免)僵屍進程

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM