Горе сигнала Python: обработчик SIGQUIT задерживает выполнение, если SIGQUIT получен во время выполнения другого обработчика сигнала? - PullRequest
9 голосов
/ 21 сентября 2008

Следующая программа очень проста: она выводит одну точку каждые полсекунды. Если он получает SIGQUIT , он переходит к выводу десяти Q с. Если он получает SIGTSTP ( Ctrl - Z ) , выдается десять Z с.

Если он получит SIGTSTP во время печати Q с, он напечатает десять Z с после того, как будет сделано десять Q s. Это хорошая вещь.

Однако, если он получает SIGQUIT во время печати Z с, он не может печатать Q с после них. Вместо этого он печатает их только после того, как я вручную прекращаю выполнение через KeyboardInterrupt. Я хочу, чтобы Q s печатались сразу после Z s.

Это происходит с использованием Python2.3.

Что я делаю не так? Muchas gracias.

#!/usr/bin/python

from signal import *
from time import sleep
from sys import stdout

def write(text):
    stdout.write(text)
    stdout.flush()

def process_quit(signum, frame):
    for i in range(10):
        write("Q")
        sleep(0.5)

def process_tstp(signum, frame):
    for i in range(10):
        write("Z")
        sleep(0.5)

signal(SIGQUIT, process_quit)
signal(SIGTSTP, process_tstp)

while 1:
    write('.')
    sleep(0.5)

Ответы [ 2 ]

6 голосов
/ 21 сентября 2008

Ваша большая проблема - блокировка в обработчиках сигналов.

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

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

#!/usr/bin/python

from signal import *
from time import sleep
from sys import stdout

print_Qs = 0
print_Zs = 0

def write(text):
    stdout.write(text)
    stdout.flush()

def process_quit(signum, frame):
     global print_Qs
     print_Qs = 10

def process_tstp(signum, frame):
     global print_Zs
     print_Zs = 10

signal(SIGQUIT, process_quit)
signal(SIGTSTP, process_tstp)

while 1:
    if print_Zs:
        print_Zs -= 1
        c = 'Z'
    elif print_Qs:
        print_Qs -= 1
        c = 'Q'
    else:
        c = '.'
    write(c)
    sleep(0.5)

В любом случае, вот что происходит.

SIGTSTP более особенный, чем SIGQUIT.

SIGTSTP маскирует другие сигналы от доставки, пока работает его обработчик сигналов. Когда ядро ​​отправляет SIGQUIT и видит, что обработчик SIGTSTP все еще работает, оно просто сохраняет его на потом. Как только поступает другой сигнал для доставки, такой как SIGINT, когда вы CTRL + C (он же KeyboardInterrupt), ядро ​​запоминает, что оно никогда не доставляло SIGQUIT, и доставляет его сейчас.

Вы заметите, что если вы измените while 1: на for i in range(60): в главном цикле и повторите тест, программа выйдет без запуска обработчика SIGTSTP, так как выход не запускает механизм доставки сигналов ядра.

Удачи!

1 голос
/ 21 сентября 2008

На Python 2.5.2 в Linux 2.6.24 ваш код работает точно так же, как вы описываете желаемые результаты (если сигнал получен, пока еще обрабатывается предыдущий сигнал, новый сигнал обрабатывается сразу после завершения первого) .

В Python 2.4.4 в Linux 2.6.16 я вижу проблему, которую вы описываете.

Я не знаю, связано ли это с изменением в Python или в ядре Linux.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...