Процессы Python перестают отвечать на SIGTERM / SIGINT после перезапуска - PullRequest
3 голосов
/ 16 июля 2009

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

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

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

Однако при запуске из сторожевого процесса дочерний процесс завершится только после сигнала FIRST . Когда сторожевой таймер перезапускает дочерний процесс, новый дочерний процесс больше не отвечает на 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)

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

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

Ответы [ 2 ]

5 голосов
/ 16 июля 2009

Как объяснено здесь: 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
0 голосов
/ 16 июля 2009

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

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

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

...