Python: Как определить, что дочерние процессы подпроцесса закончили работу - PullRequest
5 голосов
/ 24 мая 2011

Я пытаюсь определить, когда программа установки завершает выполнение из скрипта Python.В частности, это база данных Oracle 10gR2.В настоящее время я использую модуль подпроцесса с Popen.В идеале, я бы просто использовал метод wait (), чтобы дождаться завершения установки, однако документированная команда фактически порождает дочерние процессы для обработки фактической установки.Вот пример кода с ошибочным кодом:

import subprocess
OUI_DATABASE_10GR2_SUBPROCESS = ['sudo',
                                 '-u',
                                 'oracle',
                                 os.path.join(DATABASE_10GR2_TMP_PATH,
                                              'database',
                                              'runInstaller'),
                                 '-ignoreSysPrereqs',
                                 '-silent',
                                 '-noconfig',
                                 '-responseFile '+ORACLE_DATABASE_10GR2_SILENT_RESPONSE]
oracle_subprocess = subprocess.Popen(OUI_DATABASE_10GR2_SUBPROCESS)
oracle_subprocess.wait()

Здесь есть похожий вопрос: Удаление подпроцесса, включая его дочерние элементы из python , но выбранный ответ не обращается к дочернимвместо этого он указывает пользователю вызвать приложение непосредственно для ожидания.Я ищу конкретное решение, которое будет ждать всех детей подпроцесса.Что если существует неизвестное количество подпроцессов?Я выберу ответ, который решает проблему ожидания завершения всех дочерних подпроцессов.

Больше ясности при сбое: дочерние процессы продолжают выполняться после команды wait (), так как эта команда ожидает только процесс верхнего уровня(в данном случае это «sudo»).Вот простая диаграмма известных дочерних процессов в этой задаче: Модуль подпроцесса Python -> Sudo -> runInstaller -> java -> (unknown)

Ответы [ 4 ]

3 голосов
/ 24 мая 2011

Хорошо, вот трюк, который будет работать только под Unix. Это похоже на один из ответов на этот вопрос: Гарантия того, что подпроцессы не работают при выходе из программы Python . Идея состоит в том, чтобы создать новую группу процессов. Затем вы можете дождаться завершения всех процессов в группе.

pid = os.fork()
if pid == 0:
    os.setpgrp()
    oracle_subprocess = subprocess.Popen(OUI_DATABASE_10GR2_SUBPROCESS)
    oracle_subprocess.wait()
    os._exit(0)
else:
    os.waitpid(-pid)

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

Я нашел эту веб-страницу также полезной. http://code.activestate.com/recipes/278731-creating-a-daemon-the-python-way/

2 голосов
/ 12 июля 2011

Проверьте следующую ссылку http://www.oracle -wiki.net / startdocsruninstaller , в которой указан флаг, который можно использовать для команды runInstaller.

Этот флаг определенно доступен для 11gR2, но у меня нет базы данных 10g, чтобы проверить этот флаг для runInstaller, поставляемого с этой версией.

С уважением

2 голосов
/ 24 мая 2011

Вы можете просто использовать os.waitpid с pid, установленным в -1, это будет ожидать всех подпроцессов текущего процесса, пока они не завершатся:является результатом pstree <pid> разветвления другого подпроцесса:

python───python───sh───sleep

Надеюсь, что это может помочь:)

1 голос
/ 27 мая 2011

Везде, куда я смотрю, кажется, что это невозможно решить в общем случае. Я собрал библиотеку pidmon, которая объединяет некоторые ответы для Windows и Linux и может сделать то, что вам нужно.

Я планирую почистить это и поместить на github, возможно, под названием 'pidmon' или что-то в этом роде. Я опубликую ссылку, если / когда получу ее.

РЕДАКТИРОВАТЬ: Это доступно в http://github.com/dbarnett/python-pidmon.

Я создал специальную функцию waitpid, которая принимает аргумент graft_func, чтобы вы могли свободно определять, какого рода процессы вы хотите ожидать, когда они не являются прямыми дочерними элементами:

import pidmon
pidmon.waitpid(oracle_subprocess.pid, recursive=True,
        graft_func=(lambda p: p.name == '???' and p.parent.pid == ???))

или, в качестве дробовика, просто подождать, пока не начнутся какие-либо процессы после повторного вызова waitpid, выполнить:

import pidmon
pidmon.waitpid(oracle_subprocess.pid, graft_func=(lambda p: True))

Обратите внимание, что это все еще едва проверено в Windows и кажется очень медленным в Windows (но я упоминал, что это на github, где его легко раскошелиться?). По крайней мере, это должно помочь вам начать работу, и если у вас это сработает, у меня будет множество идей по его оптимизации.

...