Подтвердить что ты не робот

Как убивать (или избегать) процессы зомби с модулем подпроцесса

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

Есть ли способ убить подпроцесс, не убивая родителя? Я знаю, что могу сделать это, используя wait(), но мне нужно запустить my script с no_wait().

4b9b3361

Ответ 1

Не использовать 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

Ответ 2

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

Вероятно, нам нужна дополнительная информация о процессах, которые вы запускаете, чтобы реально помочь.

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

Ответ 3

Если вы удалите объект подпроцесса, используя del, чтобы принудительно собрать мусор, это приведет к удалению объекта подпроцесса, а затем исчезнут процессы, которые не будут прекращены, без прерывания вашего интерпретатора. Сначала вы можете попробовать это в интерфейсе командной строки python.

Ответ 4

Среда выполнения python берет на себя ответственность за избавление от процесса зомби, когда их объекты процесса были собраны в мусор. Если вы видите, что зомби лежит вокруг, это означает, что вы сохранили объект процесса и не вызвали ожидание, опрос или завершение на нем.

Ответ 5

Если вы просто используете 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() для объекта, возвращаемого Popen, чтобы проверить, завершено ли оно (без ожидания). Если он возвращает 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().

Ответ 6

Я не уверен, что вы имеете в виду "Мне нужно запустить my script с 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

Ответ 7

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

os.wait3(os.WNOHANG)

Ответ 8

Недавно я столкнулся с этой проблемой зомби из-за моего python script. Фактическая проблема была в основном из-за убийства подпроцесса, и родительский процесс не знал, что ребенок мертв. Так что я сделал это, просто добавив popen.communicate() после сигнала kill дочернего процесса, чтобы родительский процесс узнал, что ребенок мертв, а затем ядро ​​обновляет pid дочернего процесса, так как ребенка больше нет и поэтому теперь не образуются зомби.

PS: опрос также является опцией здесь, поскольку он проверяет и передает родительский статус дочернего элемента. Часто в подпроцессе лучше использовать u check_output или вызывать, если вам не нужно связываться с stdout и stdin.