Ошибка при использовании многопроцессорного модуля в демоне Python - PullRequest
6 голосов
/ 01 сентября 2009

Я получаю следующую ошибку при использовании модуля multiprocessing в процессе демона python (с использованием python-daemon ):

Traceback (most recent call last):
  File "/usr/local/lib/python2.6/atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)
  File "/usr/local/lib/python2.6/multiprocessing/util.py", line 262, in _exit_function
    for p in active_children():
  File "/usr/local/lib/python2.6/multiprocessing/process.py", line 43, in active_children
    _cleanup()
  File "/usr/local/lib/python2.6/multiprocessing/process.py", line 53, in _cleanup
    if p._popen.poll() is not None:
  File "/usr/local/lib/python2.6/multiprocessing/forking.py", line 106, in poll
    pid, sts = os.waitpid(self.pid, flag)
OSError: [Errno 10] No child processes

Демон-процесс (родитель) порождает несколько процессов (дочерних процессов), а затем периодически опрашивает процессы, чтобы узнать, завершены ли они. Если родитель обнаруживает, что один из процессов завершен, он пытается перезапустить этот процесс. Именно в этот момент возникает вышеупомянутое исключение. Похоже, что после завершения одного из процессов любая операция, включающая многопроцессорный модуль, сгенерирует это исключение. Если я запускаю идентичный код в скрипте, не являющемся демоном, он выполняется без ошибок.

EDIT:

Пример сценария

from daemon import runner

class DaemonApp(object):
    def __init__(self, pidfile_path, run):
        self.pidfile_path = pidfile_path
        self.run = run

        self.stdin_path = '/dev/null'
        self.stdout_path = '/dev/tty'
        self.stderr_path = '/dev/tty'

def run():
    import multiprocessing as processing
    import time
    import os
    import sys
    import signal

    def func():
        print 'pid: ', os.getpid()
        for i in range(5):
            print i
            time.sleep(1)

    process = processing.Process(target=func)
    process.start()

    while True:
        print 'checking process'
        if not process.is_alive():
            print 'process dead'
            process = processing.Process(target=func)
            process.start()
        time.sleep(1)

# uncomment to run as daemon
app = DaemonApp('/root/bugtest.pid', run)
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()

#uncomment to run as regular script
#run()

Ответы [ 6 ]

6 голосов
/ 16 сентября 2009

Ваша проблема - конфликт между демоном и многопроцессорными модулями, в частности, при обработке сигнала SIGCLD (дочерний процесс завершен).демон устанавливает SIGCLD на SIG_IGN при запуске, что, по крайней мере в Linux, приводит к немедленному пожинанию завершенных потомков (вместо того, чтобы становиться зомби, пока родитель не вызовет wait ()).Но тест is_alive в многопроцессорном режиме вызывает wait (), чтобы увидеть, жив ли процесс, который завершается неудачей, если процесс уже был получен.

Самое простое решение - просто вернуть SIGCLD в SIG_DFL (поведение по умолчанию - игнорировать сигнали позвольте родителю ждать () завершенного дочернего процесса):

def run():
    # ...

    signal.signal(signal.SIGCLD, signal.SIG_DFL)

    process = processing.Process(target=func)
    process.start()

    while True:
        # ...
4 голосов
/ 17 сентября 2009

Игнорирование SIGCLD также вызывает проблемы с модулем subprocess из-за ошибки в этом модуле ( выпуск 1731717 , все еще открытый на 2011-09-21).

Это поведение рассматривается в версии 1.4.8 библиотеки python-daemon; теперь он опускает перестановку по умолчанию с SIGCLD, поэтому больше не имеет этого неприятного взаимодействия с другими модулями стандартной библиотеки.

0 голосов
/ 15 сентября 2009

Исходный пример скрипта имеет «сигнал импорта», но не использует сигналы. Тем не менее, у меня был скрипт, вызывающий это сообщение об ошибке, и это было связано с моей обработкой сигнала, поэтому я объясню здесь на случай, что происходит с другими. В обработчике сигналов я занимался процессами (например, создавал новый процесс). По-видимому, это не работает, поэтому я перестал делать это в обработчике и исправил ошибку. (Примечание: функции sleep () активируются после обработки сигналов, так что это может быть альтернативный подход к действию на сигналы, если вам нужно что-то делать с процессами)

0 голосов
/ 14 сентября 2009

Я сталкиваюсь с этим также с помощью распределенного менеджера задач сельдерея под RHEL 5.3 с Python 2.6. Мой traceback выглядит немного иначе, но ошибка та же:

      File "/usr/local/lib/python2.6/multiprocessing/pool.py", line 334, in terminate
    self._terminate()
  File "/usr/local/lib/python2.6/multiprocessing/util.py", line 174, in __call__
    res = self._callback(*self._args, **self._kwargs)
  File "/usr/local/lib/python2.6/multiprocessing/pool.py", line 373, in _terminate_pool
    p.terminate()
  File "/usr/local/lib/python2.6/multiprocessing/process.py", line 111, in terminate
    self._popen.terminate()
  File "/usr/local/lib/python2.6/multiprocessing/forking.py", line 136, in terminate
    if self.wait(timeout=0.1) is None:
  File "/usr/local/lib/python2.6/multiprocessing/forking.py", line 121, in wait
    res = self.poll()
  File "/usr/local/lib/python2.6/multiprocessing/forking.py", line 106, in poll
    pid, sts = os.waitpid(self.pid, flag)
OSError: [Errno 10] No child processes

Довольно разочаровывает .. Я сейчас запускаю код через pdb, но пока ничего не заметил.

0 голосов
/ 01 сентября 2009

Похоже, ваша ошибка приходит в самом конце вашего процесса - ваша подсказка в самом начале вашего отслеживания, и я цитирую ...:

File "/usr/local/lib/python2.6/atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)

если atexit._run_exitfuncs запущен, это ясно показывает, что ваш собственный процесс завершается. Таким образом, сама ошибка является в некотором смысле незначительной проблемой - просто из-за какой-то функции, которую модуль multiprocessing зарегистрировал для запуска «на выходе» из вашего процесса. Действительно интересный вопрос: ПОЧЕМУ ваш основной процесс завершается? Я думаю, что это может быть связано с каким-то неперехваченным исключением: попробуйте установить ловушку исключения и показать расширенную диагностическую информацию, прежде чем она будет потеряна из-за ДРУГОГО исключения, вызванного тем, что многопроцессорная система зарегистрирована для запуска на выходе ...

0 голосов
/ 01 сентября 2009

Я думаю, что некоторое время назад было исправление в trunk и 2.6 maint, что должно помочь в этом, можете ли вы попробовать запустить скрипт в python-trunk или в последней версии 2.6-maint svn? Я не могу получить информацию об ошибке

...