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

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

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

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

Это происходит с использованием 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)
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
9
0
4 299
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

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

В любом случае, вот как хотя бы минимизировать условие синхронизации, установив только флаги в ваших обработчиках и оставив основной цикл 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, поскольку exit не запускает повторно механизм доставки сигнала ядра.

Удачи!

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

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

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

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