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

У меня есть длительный скрипт Python с рабочим подпроцессом perl. Данные отправляются в дочерний процесс и из него через его стандартный ввод и стандартный вывод. Периодически ребенка необходимо перезагружать.

К сожалению, через некоторое время работы у него заканчиваются файлы («слишком много открытых файлов»). 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
Когда вы убиваете его, может ли он вызывать OSError по какой-либо причине, кроме того, что он уже мертв?   -  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
@Дж.Ф. Себастьян, я добавил close_fds и посмотрю, но похоже, что это очищает fds от главного процесса в дочернем, а не наоборот?   -  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