Подтвердить что ты не робот

Python-daemon не убивает своих детей

При использовании python-daemon я создаю подпроцессы liko:

import multiprocessing

class Worker(multiprocessing.Process):
   def __init__(self, queue):
      self.queue = queue # we wait for things from this in Worker.run()

   ...

q = multiprocessing.Queue()

with daemon.DaemonContext():
    for i in xrange(3):
       Worker(q)

    while True: # let the Workers do their thing
       q.put(_something_we_wait_for())

Когда я убиваю родительский демонический процесс (т.е. не рабочий) с помощью Ctrl-C или SIGTERM и т.д., дети не умирают. Как убить детей?

Моя первая мысль состоит в том, чтобы использовать atexit, чтобы убить всех работников, likeso:

 with daemon.DaemonContext():
    workers = list()
    for i in xrange(3):
       workers.append(Worker(q))

    @atexit.register
    def kill_the_children():
        for w in workers:
            w.terminate()

    while True: # let the Workers do their thing
       q.put(_something_we_wait_for())

Тем не менее, дети демонов - это сложные вещи, которые нужно обработать, и я буду обязан мыслями и информацией о том, как это должно быть сделано.

Спасибо.

4b9b3361

Ответ 1

Ваши варианты немного ограничены. Если выполнение self.daemon = True в конструкторе для класса Worker не решит вашу проблему, и попытка поймать сигналы в родительском элементе (т.е. SIGTERM, SIGINT) не работает, возможно, вам придется попробовать противоположное решение - вместо если родитель убивает детей, вы можете заставить детей совершить самоубийство, когда родитель умирает.

Первый шаг - дать конструктору Worker PID родительского процесса (вы можете сделать это с помощью os.getpid()). Затем вместо выполнения self.queue.get() в рабочем цикле выполните следующее:

waiting = True
while waiting:
    # see if Parent is at home
    if os.getppid() != self.parentPID:
        # woe is me! My Parent has died!
        sys.exit() # or whatever you want to do to quit the Worker process
    try:
        # I picked the timeout randomly; use what works
        data = self.queue.get(block=False, timeout=0.1)
        waiting = False
    except queue.Queue.Empty:
        continue # try again
# now do stuff with data

Вышеприведенное решение проверяет, отличается ли родительский PID, чем он был первоначально (то есть, если дочерний процесс был принят init или lauchd, потому что родитель умер) - см. ссылка. Однако, если это не работает по какой-либо причине, вы можете заменить его следующей функцией (адаптированной из здесь):

def parentIsAlive(self):
    try:
        # try to call Parent
        os.kill(self.parentPID, 0)
    except OSError:
        # *beeep* oh no! The phone disconnected!
        return False
    else:
        # *ring* Hi mom!
        return True

Теперь, когда Родитель умирает (по какой-либо причине), ребенок Рабочие будут спонтанно падать, как мухи - так, как вы хотели, вы демон! :-D

Ответ 2

Вы не можете просто сохранить родительский pid, когда ребенок сначала создан (скажем, в self.myppid), а когда self.myppid отличается от getppid() означает, что родитель умер.

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

Ответ 3

Atexit не будет делать трюк - он запускается только при успешном завершении без сигнала - см. примечание в верхней части docs. Вам необходимо настроить обработку сигнала одним из двух способов.

Более простая настройка: установите флаг daemon в ваших рабочих процессах, за http://docs.python.org/library/multiprocessing.html#process-and-exceptions

Несколько более сложный вариант: PEP-3143, по-видимому, подразумевает, что есть встроенный способ перехвата запросов на очистку программ в python- демон.