Linux блокирует сигналы для инициализации Python - PullRequest
9 голосов
/ 01 мая 2011

Это продолжение моего другого поста Установка обработчика сигнала с Python .Короче говоря, Linux блокирует все сигналы на PID 1 (включая SIGKILL), если Init не установил обработчик сигнала для определенного сигнала;чтобы предотвратить панику ядра, если кто-то отправит сигнал завершения на PID1.Проблема, с которой я столкнулся, заключается в том, что модуль signal в Python не устанавливает обработчики сигналов способом, который распознает система.Мой скрипт Python Init, казалось, полностью игнорировал все сигналы, так как я думаю, что они были заблокированы.

Кажется, я нашел решение;используя ctypes для установки обработчиков сигналов с функцией signal() в libc (в данном случае uClibc).Ниже приведен пример теста на основе Python.На TTY2 открывается оболочка, из которой я могу отправлять сигналы на PID1 для тестирования.Кажется, он работает в KVM, который я использую для тестирования (я готов поделиться виртуальной машиной со всеми заинтересованными лицами)

Это лучший способ обойти эту проблему?Есть ли «лучший» способ установки обработчиков сигналов без сигнального модуля?(Я вообще не занимаюсь переносимостью)

Это ошибка в Python?

#!/usr/bin/python

import os
import sys
import time

from ctypes import *

def SigHUP():
    print "Caught SIGHUP"
    return 0

def SigCHLD():
    print "Caught SIGCHLD"
    return 0

SIGFUNC = CFUNCTYPE(c_int)
SigHUPFunc = SIGFUNC(SigHUP)
SigCHLDFunc = SIGFUNC(SigCHLD)

libc = cdll.LoadLibrary('libc.so.0')
libc.signal(1, SigHUPFunc) # 1 = SIGHUP
libc.signal(17, SigCHLDFunc) # 17 = SIGCHLD

print "Mounting Proc: %s" % libc.mount(None, "/proc", "proc", 0, None)

print "forking for ash"
cpid = os.fork()
if cpid == 0:
    os.closerange(0, 4)
    sys.stdin = open('/dev/tty2', 'r')
    sys.stdout = open('/dev/tty2', 'w')
    sys.stderr = open('/dev/tty2', 'w')
    os.execv('/bin/ash', ('ash',))

print "ash started on tty2"

print "sleeping"
while True:
    time.sleep(0.01)

Ответы [ 2 ]

7 голосов
/ 01 мая 2011

Я немного отладил в KVM и обнаружил, что ядро ​​ - это , доставляющее сигналы на pid 1, когда обработчики сигналов установлены стандартным сигнальным модулем. Однако при получении сигнала «что-то» вызывает клон процесса, а не выводит ожидаемый результат.

Вот вывод strace, когда я отправляю HUP в нерабочий init.sig-mod:

strace output

В результате запускается новый процесс (pid 23), который является клоном init.sig-mod:

clone of init as pid 23

У меня не было времени углубиться в причину, но это сужает вещи дальше. Возможно, это связано с логикой доставки сигналов Python (она регистрирует ловушку C, которая вызывает вашу функцию байт-кода при вызове). Техника ctypes обходит это. Соответствующие исходные файлы Python: Python / pythonrun.c и Modules / signalmodule.c , на случай, если вы захотите взглянуть поближе.

Старая информация - Я не уверен, что это решит вашу проблему, но может приблизить вас. я сравнил эти разные способы установки обработчиков сигналов:

  • Установка обработчика через сигнальный модуль Python.
  • Обработчики сигналов Upstart.
  • Использование ctypes для непосредственного вызова системного вызова signal().
  • Несколько быстрых тестов на C.

И системный вызов, вызванный ctypes signal(), и Upstart sigaction() Системные вызовы устанавливают флаг SA_RESTART, когда обработчик зарегистрирован. настройка этот флаг указывает, что, когда сигнал получен, в то время как процесс выполнение или блокировка внутри определенных системных вызовов (чтение, запись, ожидание, nanosleep и т. д.), после завершения обработчика сигнала системный вызов должен быть автоматически перезагружается. Приложение не будет знать об этом.

Когда сигнальный модуль Python регистрирует обработчик, он обнуляет SA_RESTART пометить, позвонив siginterrupt(signum, 1). Это говорит системе "когда системный вызов прерывается сигналом после завершения обработчика сигнала установите errno на EINTR и вернитесь из системного вызова ". Это оставляет за разработчиком обработайте это и решите, следует ли перезапустить системный вызов.

Вы можете установить флаг SA_RESTART, зарегистрировав свой сигнал следующим образом:

import signal
signal.signal(signal.SIGHUP, handler)
signal.siginterrupt(signal.SIGHUP, False)
1 голос
/ 06 мая 2011

Проблема заключалась в совместимости Python, скомпилированного для uClibc 0.9.31 со старыми потоками Linux.Компиляция против 0.9.32-rc3 и использование NPTL устранили проблему.

...