как убить (или избежать) процессы зомби с помощью модуля подпроцесса - PullRequest
49 голосов
/ 03 мая 2010

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

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

Ответы [ 8 ]

22 голосов
/ 04 мая 2010

Не использование 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
19 голосов
/ 04 мая 2010

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

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

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

15 голосов
/ 18 октября 2012

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

6 голосов
/ 15 августа 2011

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

5 голосов
/ 04 июня 2011

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

1 голос
/ 08 мая 2010

Я не уверен, что вы имеете в виду "Мне нужно запустить мой скрипт с 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
0 голосов
/ 07 ноября 2016

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

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

0 голосов
/ 03 мая 2010

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

os.wait3(os.WNOHANG)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...