Как процесс Python корректно завершается после получения SIGTERM во время ожидания семафора?

У меня есть процесс Python, который порождает 5 других процессов Python, используя модуль многопроцессорности. Назовем родительский процесс P0, а остальные P1-P5. Требование состоит в том, что если мы отправим SIGTERM на P0, он должен сначала закрыть P1 на P5, а затем выйти сам.

Подвох в том, что P1 и P5 ждут на семафорах. Поэтому, когда я посылаю SIGTERM этим процессам, они вызывают обработчик сигнала и завершают работу. Но так как они ожидают на семафоре, они выдают исключение. Есть ли способ поймать это исключение перед выходом, чтобы P0-P5 мог сделать изящный выход?

Проследить:

Traceback (most recent call last):
  File "/usr/lib64/python2.7/multiprocessing/process.py", line 258, in _bootstrap
Traceback (most recent call last):
Process Process-2:
  File "/usr/lib64/python2.7/multiprocessing/process.py", line 258, in _bootstrap
Traceback (most recent call last):
self.run()
File "/usr/lib64/python2.7/multiprocessing/process.py", line 114, in run
self._target(*self._args, **self._kwargs)
Process Process-5:
Traceback (most recent call last):
File "/usr/lib64/python2.7/multiprocessing/process.py", line 258, in _bootstrap
  self.run()
File "/usr/lib64/python2.7/multiprocessing/process.py", line 114, in run
  self._target(*self._args, **self._kwargs)
File "/opt/fireeye/scripts/mip/StaticAnalysisRunner.py", line 45, in run
  qsem.acquire()

person Diptesh Chatterjee    schedule 16.10.2014    source источник
comment
Какую версию Python вы используете? Какой конкретный вызов на самом деле делают P1 и P5, который заставляет их ждать на семафоре? Можете ли вы включить трассировку?   -  person dano    schedule 17.10.2014
comment
Минимальный пример кода, воспроизводящий вашу проблему, был бы замечательным.   -  person Paulo Scardine    schedule 17.10.2014
comment
Я использую Python 2.7.8 от P1 до P5, пытаясь получить доступ к очереди, совместно используемой процессами, и доступ к очереди защищен с помощью семафора. Что-то вроде этого: sem.acquire() item = q.get() sem.release ()   -  person Diptesh Chatterjee    schedule 17.10.2014


Ответы (1)


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

Вот пример сценария, который ожидает в семафоре в подпроцессе и корректно завершается при отправке SIGTERM.

#!/usr/bin/env python

import signal
import time
import multiprocessing

class GracefulExit(Exception):
    pass


def signal_handler(signum, frame):
    raise GracefulExit()


def subprocess_function():
    try:
        sem = multiprocessing.Semaphore()
        print "Acquiring semaphore"
        sem.acquire()
        print "Semaphore acquired"

        print "Blocking on semaphore - waiting for SIGTERM"
        sem.acquire()
    except GracefulExit:
        print "Subprocess exiting gracefully"


if __name__ == "__main__":

    # Use signal handler to throw exception which can be caught to allow
    # graceful exit.
    signal.signal(signal.SIGTERM, signal_handler)

    # Start a subprocess and wait for it to terminate.
    p = multiprocessing.Process(target=subprocess_function)
    p.start()

    print "Subprocess pid: %d" % p.pid

    p.join()

Пример запуска этого скрипта выглядит следующим образом:

$ ./test.py 
Subprocess pid: 7546
Acquiring semaphore
Semaphore acquired
Blocking on semaphore - waiting for SIGTERM
----> Use another shell to kill -TERM 7546
Subprocess exiting gracefully

От подпроцесса нет обратной трассировки, и поток показывает, что подпроцесс завершается изящным образом. Это связано с тем, что SIGTERM перехватывается обработчиком сигнала подпроцесса, который выдает обычное исключение Python, которое может быть обработано внутри процесса.

person Austin Phillips    schedule 17.10.2014