как уничтожить (или избежать), процессы-зомби с модулем подпроцесса

Когда я начинаю сценарий Python из другого сценария Python с помощью модуля подпроцесса, процесс-зомби создается, когда подпроцесс "завершается". Я не могу уничтожить этот подпроцесс, если я не уничтожаю свой родительский процесс Python.

Существует ли способ уничтожить подпроцесс, не уничтожая родителя? Я знаю, что могу сделать это при помощи ожидания (), но я должен запустить свой скрипт с no_wait ().

51
задан Dave 3 May 2010 в 08:29
поделиться

4 ответа

Зомби-процесс - это не настоящий процесс; это просто оставшаяся запись в таблице процессов до тех пор, пока родительский процесс не запросит код возврата дочернего процесса. Фактический процесс завершен и не требует никаких других ресурсов, кроме указанной записи в таблице процессов.

Нам, вероятно, понадобится больше информации о выполняемых вами процессах, чтобы действительно больше помогать.

Однако в случае, если ваша программа 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 (т.е. завершенные).

20
ответ дан 7 November 2019 в 10:18
поделиться

Я не совсем понимаю, что вы имеете в виду под no_wait(). Вы имеете в виду, что нельзя блокировать ожидание завершения дочерних процессов? Если так, то я думаю, что это сделает то, что вы хотите:

os.wait3(os.WNOHANG)
0
ответ дан 7 November 2019 в 10:18
поделиться

Неиспользование Popen.communicate () или call () приведет к зомби-процессу.

Если вам не нужен вывод команды, вы можете использовать subprocess.call () :

>>> import subprocess
>>> subprocess.call(['grep', 'jdoe', '/etc/passwd'])
0

Если вывод важен, вы должны использовать Popen () и communication () , чтобы получить 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
ответ дан 7 November 2019 в 10:18
поделиться

Я не уверен, что вы имеете в виду "мне нужно запустить мой скрипт с 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
ответ дан 7 November 2019 в 10:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: