web-dev-qa-db-ja.com

サブプロセスモジュールでゾンビプロセスを強制終了(または回避)する方法

サブプロセスモジュールを使用して別のpythonスクリプト内からpythonスクリプトを開始すると、サブプロセスが「完了」するとゾンビプロセスが作成されます。親を殺さない限り、このサブプロセスを殺すpython process。

親を殺さずにサブプロセスを殺す方法はありますか? wait()を使用してこれを行うことができますが、no_wait()を使用してスクリプトを実行する必要があります。

49
Dave

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
_
22
David Narayan

ゾンビプロセスは実際のプロセスではありません。親プロセスが子のリターンコードを要求するまで、プロセステーブルの残りのエントリにすぎません。実際のプロセスは終了し、プロセステーブルエントリ以外のリソースは必要ありません。

実際に支援するために、実行するプロセスに関する詳細情報が必要になる可能性があります。

ただし、Python program knowsの場合、子プロセスが終了したとき(たとえば、子stdoutデータの最後に到達したことにより))、安全に呼び出すことができます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(つまり、完成したもの)とは異なる戻りコードを持つ子をリストから忘れずに削除してください。

19
tzot

delを使用してガベージコレクションを強制してサブプロセスオブジェクトを削除すると、サブプロセスオブジェクトが削除され、インタプリタを終了せずに無効なプロセスがなくなります。これは、最初にpythonコマンドラインインターフェイスで試してみてください。

15
user331905

単に_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 __== '__main__':
    do_some_stuff()
_

Popenによって返されたオブジェクトで.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
_

上記の例で、ゾンビを取り除きたい場合、結果がNoneでなくなるまで、各子の.wait()または.poll()のいずれかを使用できます。

どちらの方法でも構いません-参照を保持しないか、.wait()または.poll()を使用します。

6
ibz

pythonランタイムは、プロセスオブジェクトがガベージコレクションされると、ゾンビプロセスを削除する責任を負います。またはそれで終了します。

5
Peter

「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
1
Peter Lyons

no_wait()の意味がよくわかりません。子プロセスの終了を待つことをブロックできないという意味ですか?そう仮定すると、これはあなたが望むことをするだろうと思う:

os.wait3(os.WNOHANG)
0

最近、私はpythonスクリプトによるこのゾンビの問題に遭遇しました。実際の問題は、主にサブプロセスの強制終了によるもので、親プロセスは子が死んでいることを知りません。私は、子プロセスのkillシグナルの後にpopen.communicate()を追加して、親プロセスが子プロセスが死んでいることを知るようにし、子プロセスがなくなったのでカーネルが子プロセスのpidを更新するようにしました今形成されたゾンビはありません。

PS:pollも、子ステータスをチェックして親に伝えるため、ここでのオプションです。多くの場合、サブプロセスでは、check_outputを使用するか、stdoutおよびstdinと通信する必要がない場合に呼び出すとよいでしょう。