Как да почистя след subprocess.Popen?

Имам дълго работещ python скрипт с perl worker подпроцес. Данните се изпращат във и извън дъщерната процедура чрез нейните stdin и stdout. Периодично детето трябва да се рестартира.

За съжаление, след известно време на работа, файловете му свършват („твърде много отворени файлове“). lsof показва много оставащи отворени тръби.

Какъв е правилният начин за почистване след процес на Popen'd? Ето какво правя в момента:

def start_helper(self):
    # spawn perl helper
    cwd = os.path.dirname(__file__)
    if not cwd:
        cwd = '.'

    self.subp = subprocess.Popen(['perl', 'theperlthing.pl'], shell=False, cwd=cwd,
                                 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                                 bufsize=1, env=perl_env)

def restart_helper(self):
    # clean up
    if self.subp.stdin:
        self.subp.stdin.close()
    if self.subp.stdout:
        self.subp.stdout.close()
    if self.subp.stderr:
        self.subp.stderr.close()

    # kill
    try:
        self.subp.kill()
    except OSError:
        # can't kill a dead proc
        pass
    self.subp.wait() # ?

    self.start_helper()

person dkuebric    schedule 17.02.2011    source източник
comment
Popen.kill (или Popen.terminate) е най-добрият ви залог.   -  person Santa    schedule 17.02.2011
comment
Когато го убиете, може ли да повдига OSEror по някаква друга причина, освен че вече е мъртъв?   -  person Thomas K    schedule 17.02.2011
comment
ето донякъде несвързана грешка в subprocess, но може да хвърли малко светлина върху _cleanup() bugs.python.org/issue1731717   -  person jfs    schedule 17.02.2011
comment
използвайте close_fds=True (по подразбиране на py3k), ако не сте на Windows   -  person jfs    schedule 17.02.2011
comment
между другото, на някои системи има абсурдно малко ограничение за броя на отворените файлове.   -  person jfs    schedule 17.02.2011
comment
@J.F. Себастиан, добавих close_fds и ще видя, но изглежда, че това почиства fds от главния proc в дъщерния, а не обратното?   -  person dkuebric    schedule 17.02.2011
comment
@dkuebric: Не знам.   -  person jfs    schedule 17.02.2011
comment
Опитахте ли да проверите какво има в другия край на тези отворени тръби? Ето едно несложно хакване, което разчита на това, че NODE е 8-то поле на lsof изхода: lsof | grep " \($(lsof -p YOUR_PYTHON_PID | grep pipe | awk '{if (FNR > 1) {printf "%s", "\\|"} printf "%s", $8}')\) " Ако другите краища са вашите perl процеси, тогава имате изтичане по някакъв начин...   -  person rlibby    schedule 20.02.2011
comment
Мразя да го кажа, но наистина искам да знам защо perl скриптът трябва периодично да се унищожава и да се рестартира на първо място!   -  person Michael Scott Shappe    schedule 24.02.2011
comment
Значи процесът на Python изчерпва отворените файлови дескриптори? Изпълнявате ли дъщерния perl процес многократно в родителския? Не е ясно защо родителският процес би използвал допълнителни файлови дескриптори от този пример.   -  person stderr    schedule 24.02.2011
comment
Коя версия на Python и коя ОС?   -  person aknuds1    schedule 27.02.2011


Отговори (2)


Мисля, че това е всичко, от което се нуждаете:

def restart_helper(self):
    # kill the process if open
    try:
        self.subp.kill()
    except OSError:
        # can't kill a dead proc
        pass

    self.start_helper()
    # the wait comes after you opened the process
    # if you want to know how the process ended you can add
    # > if self.subp.wait() != 0:
    # usually a process that exits with 0 had no errors
    self.subp.wait()

Доколкото знам, всички файлови обекти ще бъдат затворени, преди процесът popen да бъде убит.

person Thomas Stachl    schedule 29.03.2011
comment
Wait() е ключът към това да не оставяте подпроцеси и отворени канали. - person dkagedal; 30.03.2011
comment
Мисля, че в крайна сметка това се оказа истина. - person dkuebric; 31.08.2011

Бърз експеримент показва, че x = open("/etc/motd"); x = 1 почиства след себе си и не оставя отворен файлов дескриптор. Ако изпуснете последната препратка към subprocess.Popen, тръбите изглежда остават наоколо. Възможно ли е да извиквате отново start_helper() (или дори някой друг Popen), без изрично да затваряте и спирате стария?

person Ben Jackson    schedule 01.03.2011