サブプロセスモジュールを使用して別のpythonスクリプト内からpythonスクリプトを開始すると、サブプロセスが「完了」するとゾンビプロセスが作成されます。親を殺さない限り、このサブプロセスを殺すpython process。
親を殺さずにサブプロセスを殺す方法はありますか? 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
_
ゾンビプロセスは実際のプロセスではありません。親プロセスが子のリターンコードを要求するまで、プロセステーブルの残りのエントリにすぎません。実際のプロセスは終了し、プロセステーブルエントリ以外のリソースは必要ありません。
実際に支援するために、実行するプロセスに関する詳細情報が必要になる可能性があります。
ただし、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
(つまり、完成したもの)とは異なる戻りコードを持つ子をリストから忘れずに削除してください。
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 __== '__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()
を使用します。
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
no_wait()
の意味がよくわかりません。子プロセスの終了を待つことをブロックできないという意味ですか?そう仮定すると、これはあなたが望むことをするだろうと思う:
os.wait3(os.WNOHANG)
最近、私はpythonスクリプトによるこのゾンビの問題に遭遇しました。実際の問題は、主にサブプロセスの強制終了によるもので、親プロセスは子が死んでいることを知りません。私は、子プロセスのkillシグナルの後にpopen.communicate()を追加して、親プロセスが子プロセスが死んでいることを知るようにし、子プロセスがなくなったのでカーネルが子プロセスのpidを更新するようにしました今形成されたゾンビはありません。
PS:pollも、子ステータスをチェックして親に伝えるため、ここでのオプションです。多くの場合、サブプロセスでは、check_outputを使用するか、stdoutおよびstdinと通信する必要がない場合に呼び出すとよいでしょう。