Python problemas de sinal: SIGQUIT atrasos manipulador de execução se SIGQUIT recebeu durante a execução de outro manipulador de sinal?
Pergunta
O programa a seguir é muito simples: ele produz um único ponto cada meio segundo. Se ele recebe um SIGQUIT , procede-se a dez saída Q s. Se ele recebe um SIGTSTP ( Ctrl - Z ) , ele produz dez Z s.
Se ele recebe um SIGTSTP durante a impressão Q é, ele irá imprimir dez Z é depois que é feito com os dez Q s. Isso é uma coisa boa.
No entanto, se ele recebe um SIGQUIT durante a impressão Z é, ele não consegue imprimir Q é depois deles. Em vez disso, ele imprime-los somente depois que eu terminar manualmente a execução através de um KeyboardInterrupt. Eu quero que o Q é a ser impresso imediatamente após o Z s.
Isso acontece usando python2.3.
O que estou fazendo de errado? 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)
Solução
Seu maior problema é o bloqueio em manipuladores de sinal.
Isso geralmente é desencorajado, uma vez que pode levar a condições de tempo estranhos. Mas não é exatamente a causa do seu problema uma vez que a condição de tempo que você está vulnerável a existe por causa de sua escolha de manipuladores de sinais.
De qualquer forma, aqui está como, pelo menos, minimizar a condição de tempo, definindo única bandeiras em seus manipuladores e deixando o principal loop while para fazer o trabalho real. A explicação para por que seu código está se comportando estranhamente é descrito após o código.
#!/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)
De qualquer forma, aqui está o que está acontecendo.
SIGTSTP é mais especial do que SIGQUIT.
máscaras SIGTSTP os outros sinais de ser entregue, enquanto o seu manipulador de sinal está sendo executado. Quando o kernel vai entregar SIGQUIT e vê que manipulador de SIGTSTP ainda está em execução, ele simplesmente salva-lo para mais tarde. Uma vez que um outro sinal vem através de entrega, como SIGINT quando você Ctrl + C (aka KeyboardInterrupt), o kernel lembra que nunca entregues SIGQUIT e entrega-lo agora.
Você vai notar se você mudar while 1:
para for i in range(60):
no circuito principal e fazer o seu caso de teste novamente, o programa vai sair sem executar o manipulador SIGTSTP desde a saída não re-trigger mecanismo de entrega de sinal do kernel.
Boa sorte!
Outras dicas
Em Python 2.5.2 no Linux 2.6.24, o código funciona exatamente como você descreve os resultados desejados (se um sinal é recebido enquanto ainda processamento de um sinal anterior, o novo sinal é processado imediatamente após o primeiro é terminado) .
Em Python 2.4.4 no Linux 2.6.16, eu ver o comportamento problema que você descreve.
Eu não sei se isso é devido a uma mudança no Python ou no kernel do Linux.