Процессы Python перестают отвечать на SIGTERM / SIGINT после перезапуска

StackOverflow https://stackoverflow.com/questions/1133693

  •  16-09-2019
  •  | 
  •  

Вопрос

У меня странная проблема с некоторыми процессами python, запущенными с использованием сторожевого процесса.

Процесс watchdog написан на python и является родительским, а также имеет вызываемую функцию start_child(имя) start_child который использует подпроцесс.Всплывающий чтобы открыть дочерний процесс.Объект Popen записывается таким образом, чтобы сторожевой таймер мог отслеживать процесс с помощью опрос() и в конце концов покончить с этим завершить() когда это необходимо.Если ребенок неожиданно умирает, сторожевой пес вызывает start_child(имя) start_child снова и записывает новый всплывающий объект.

Существует 7 дочерних процессов, все из которых также являются python.Если я запущу любой из дочерних элементов вручную, я могу отправить SIGTERM или SIGINT с помощью убить и получаю результаты, которые я ожидаю (процесс завершается).

Однако при запуске из сторожевого процесса дочерний процесс завершится только после Первый сигнал.Когда сторожевой таймер перезапускает дочерний процесс, новый дочерний процесс больше не отвечает на SIGTERM или SIGINT.Я понятия не имею, что является причиной этого.

watchdog.py

class watchdog:
    # <snip> various init stuff

    def start(self):
        self.running = true

        kids = ['app1', 'app2', 'app3', 'app4', 'app5', 'app6', 'app7']
        self.processes = {}

        for kid in kids:
            self.start_child(kid)

        self.thread = threading.Thread(target=self._monitor)
        self.thread.start()

        while self.running:
            time.sleep(10)

    def start_child(self, name):
        try:
            proc = subprocess.Popen(name)
            self.processes[name] = proc
        except:
            print "oh no"
        else:
            print "started child ok"

    def _monitor(self):
        while self.running:
            time.sleep(1)
            if self.running:
                for kid, proc in self.processes.iteritems():
                    if proc.poll() is not None: # process ended
                        self.start_child(kid)

Итак , что происходит, это сторожевой таймер.start() запускает все 7 процессов, и если я отправляю какой-либо процесс SIGTERM, он завершается, и поток монитора запускает его снова.Однако, если я затем отправлю новый процесс SIGTERM, он проигнорирует его.

Я должен быть в состоянии продолжать отправлять kill -15 перезапущенным процессам снова и снова.Почему они игнорируют это после перезапуска?

Это было полезно?

Решение

Как объяснено здесь: http://blogs.gentoo.org/agaffney/2005/03/18/python_sucks , когда Python создает новый поток, он блокирует все сигналы для этого потока (и для любых процессов, порождаемых этим потоком).

Я исправил это с помощью sigprocmask, вызываемой через ctypes.Это может быть, а может и не быть "правильным" способом сделать это, но он действительно работает.

В дочернем процессе, во время __init__:

libc = ctypes.cdll.LoadLibrary("libc.so")
mask = '\x00' * 17 # 16 byte empty mask + null terminator 
libc.sigprocmask(3, mask, None) # '3' on FreeBSD is the value for SIG_SETMASK

Другие советы

Не лучше ли было бы восстановить обработчики сигналов по умолчанию в Python, а не через ctypes?В вашем дочернем процессе используйте модуль signal:

import signal
for sig in range(1, signal.NSIG):
    try:
        signal.signal(sig, signal.SIG_DFL)
    except RuntimeError:
        pass

RuntimeError возникает при попытке установить сигналы, такие как SIGKILL, которые не могут быть перехвачены.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top