2008-09-20 6 views
9

Le programme suivant est très simple: il sort un point par demi-seconde. S'il reçoit un SIGQUIT, il procède à la sortie dix Q s. Si elle reçoit un SIGTSTP(Ctrl - Z), il émet dix Z s.Malheurs du signal Python: Le gestionnaire SIGQUIT retarde l'exécution si SIGQUIT est reçu pendant l'exécution d'un autre gestionnaire de signal?

Si elle reçoit un SIGTSTP lors de l'impression Q s, il imprimera dix Z s après cela se fait avec les dix Q s. C'est une bonne chose.

Cependant, si elle reçoit un SIGQUIT lors de l'impression Z s, il ne parvient pas à imprimer Q s après eux. Au lieu de cela, il ne les affiche qu'après avoir terminé manuellement l'exécution via un KeyboardInterrupt. Je veux que le Q s soit imprimé immédiatement après le Z s.

Cela se produit en utilisant Python2.3.

Qu'est-ce que je fais mal? Merci beaucoup.

#!/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) 

Répondre

6

Votre plus gros problème se bloque dans les gestionnaires de signaux.

Ceci est généralement déconseillé car il peut conduire à des conditions de synchronisation étranges. Mais ce n'est pas tout à fait la cause de votre problème, car la condition de synchronisation que vous êtes vulnérable existe en raison de votre choix de gestionnaires de signaux.

Quoi qu'il en soit, voici comment minimiser la condition de minutage en définissant uniquement des drapeaux dans vos gestionnaires et en laissant la boucle while principale pour effectuer le travail. L'explication de la raison pour laquelle votre code se comporte étrangement est décrite après le code.

#!/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) 

Quoi qu'il en soit, voici ce qui se passe.

SIGTSTP est plus spécial que SIGQUIT. SIGTSTP masque les autres signaux qui sont délivrés pendant que le gestionnaire de signaux est en cours d'exécution. Quand le noyau va livrer SIGQUIT et voit que le gestionnaire de SIGTSTP est toujours en cours d'exécution, il le sauvegarde simplement pour plus tard. Une fois qu'un autre signal arrive à la livraison, tel que SIGINT lorsque vous CTRL + C (aka KeyboardInterrupt), le noyau se souvient qu'il n'a jamais livré SIGQUIT et le livre maintenant.

Vous remarquerez si vous changez while 1: à dans la boucle principale et de faire à nouveau votre cas de test, le programme quittera sans courir le gestionnaire SIGTSTP depuis la sortie ne redéclencher pas le mécanisme de distribution du signal du noyau.

Bonne chance!

1

Sur Python 2.5.2 sous Linux 2.6.24, votre code fonctionne exactement comme vous décrivez vos résultats souhaités (si un signal est reçu alors que vous traitez un signal précédent, le nouveau signal est traité immédiatement après que le premier est terminé).

Sur Python 2.4.4 sous Linux 2.6.16, je vois le problème que vous décrivez.

Je ne sais pas si cela est dû à une modification de Python ou du noyau Linux.

Questions connexes