Python: неблокирующий + несуществующий процесс
Я хотел бы создать родительский процесс, который будет создавать много дочерних процессов. Поскольку родительский процесс отвечает за создание дочернего процесса, родительский процесс не будет заботиться о статусе дочерних процессов.
Поскольку subprocess.call блокируется, он не работает. Поэтому я использую subprocess.Popen для замены вызова. Тем не менее, Popen будет генерировать зомби (несуществующий) процесс, как только ребенок завершит свою работу ( ссылка).
Есть ли способ решить эту проблему?
заранее спасибо
4 ответа
Есть много способов справиться с этим. Ключевым моментом является то, что зомби / "несуществующие" процессы существуют, так что родительский процесс может собирать свои статусы.
Как создатель процесса, вы можете объявить о своем намерении игнорировать статус. Метод POSIX - установить флаг
SA_NOCLDWAIT
(с помощьюsigaction
). Это немного болезненно в Python; но большинство Unix-подобных систем позволяют вам просто игнорироватьSIGCHLD
/SIGCLD
(написание варьируется от одной Unix-подобной системы к другой), что легко сделать в Python:import signal
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
Или, если это не доступно по какой-либо причине или не работает в вашей системе, вы можете использовать старый трюк в режиме ожидания: не просто разветвляться один раз, разветвляться дважды. В первом ребенке разветвите второго ребенка; у второго ребенка используйте
execve
(или аналогичный) для запуска желаемой программы; а затем в первом дочернем случае, выход (с_exit
). В оригинальном родителе используйтеwait
или жеwaidpid
или то, что предоставляет ОС, и собирать статус первого ребенка.Причина, по которой это работает, заключается в том, что второй ребенок теперь стал "сиротой" (его родитель, первый ребенок, умер и был собран вашим первоначальным процессом). Как сирота он передается родительскому доверенному лицу (в частности, "init"), который всегда
wait
и сразу собирает всех зомби.В дополнение к двойному форку, вы можете заставить ваши подпроцессы жить в своем отдельном сеансе и / или отказаться от управления доступом к терминалу ("daemonize", в терминах Unix-y). (Это немного грязно и зависит от ОС; я кодировал это раньше, но для некоторого корпоративного кода у меня нет доступа сейчас.)
Наконец, вы можете просто периодически собирать эти процессы. Если вы используете
subprocess
модуль, просто позвоните.poll
функция на каждом процессе, когда это кажется удобным. Это вернетсяNone
если процесс все еще выполняется, и состояние выхода (собрав его), если он завершен. Если некоторые из них все еще работают, ваша основная программа может выйти в любом случае, пока они продолжают работать; в этот момент они становятся сиротами, как в методе № 2 выше.
Метод ignore SIGCHLD прост и легок, но имеет недостаток, заключающийся в том, что он мешает подпрограммам библиотеки, которые создают подпроцессы и ожидают их. В Python 2.7 и более поздних версиях есть обходной путь ( http://bugs.python.org/issue15756), но это означает, что подпрограммы библиотеки не могут увидеть никаких сбоев в этих подпроцессах.
[Редактировать: http://bugs.python.org/issue1731717 для p.wait()
, где p
это процесс из subprocess.Popen
; 15756 специально для p.poll()
; но в любом случае, если у вас нет исправлений, вы должны прибегнуть к методам 2, 3 или 4.]
После завершения или уничтожения процесса операционная система ожидает, пока родительский процесс соберет статус дочернего процесса. Вы можете использовать метод process () для сбора статуса:
p = subprocess.Popen( ... )
p.terminate()
p.communicate()
Обратите внимание, что завершение процесса позволяет процессу перехватывать сигнал завершения и делать с ним все, что захочет. Это очень важно, поскольку p.communicate() является блокирующим вызовом.
Если вы не хотите этого, используйте p.kill() вместо p.terminate(), что позволяет процессу не перехватывать сигнал.
Если вы хотите использовать p.terminate() и быть уверенным, что процесс завершился сам, вы можете использовать модуль psutil для проверки состояния процесса.
Методы Торека в порядке!
Я нашел другой способ справиться с несуществующим процессом;
мы можем использовать waitpid для перезапуска несуществующего процесса по мере необходимости:
import os, subprocess, time
def recycle_pid():
while True:
try:
pid, status, _ = os.wait3(os.WNOHANG)
if pid == 0:
break
print("----- child %d terminated with status: %d" %(pid, status))
except OSError,e:
break
print("+++++ start pid:", subprocess.Popen("ls").pid)
recycle_pid()
print("+++++ start pid:", subprocess.Popen("ls").pid)
recycle_pid()
time.sleep(1)
recycle_pid()
recycle_pid не блокирует, может вызывать при необходимости.
Пожалуйста, посмотрите на http://docs.python.org/2/library/multiprocessing.html
Он предоставляет API, который очень похож на потоки. Вы можете дождаться завершения дочернего процесса, если хотите.