Проблемы с сигналом Python:Обработчик SIGQUIT задерживает выполнение, если SIGQUIT получен во время выполнения другого обработчика сигнала?

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

  •  02-07-2019
  •  | 
  •  

Вопрос

Следующая программа очень проста:он выводит одну точку каждые полсекунды.Если он получит СИГНАЛИЗАЦИЯ, он переходит к выводу десяти вопросс.Если он получит СИГТСТП (Ctrl-З), он выводит десять Зс.

Если он получит СИГТСТП во время печати вопросs, он напечатает десять Зпосле того, как закончим с десяткой вопросс.Это хорошая вещь.

Однако, если он получит СИГНАЛИЗАЦИЯ во время печати Зs, он не может печатать вопросза ними.Вместо этого он распечатывает их только после того, как я вручную прерву выполнение с помощью KeyboardInterrupt.Я хочу вопросs, которые будут распечатаны сразу после Зс.

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

Что я делаю не так?Большое спасибо.

#!/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)
Это было полезно?

Решение

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

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

В любом случае, вот как, по крайней мере, минимизировать условие синхронизации, устанавливая только флаги в ваших обработчиках и оставляя основной цикл 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+С (он же KeyboardInterrupt), ядро ​​запоминает, что оно никогда не доставляло SIGQUIT, и доставляет его сейчас.

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

Удачи!

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

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

В Python 2.4.4 в Linux 2.6.16 я вижу описанное вами проблемное поведение.

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

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