Problèmes de signal Python: le gestionnaire SIGQUIT retarde-t-il l'exécution si un SIGQUIT est reçu pendant l'exécution d'un autre gestionnaire de signal?

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

  •  02-07-2019
  •  | 
  •  

Question

Le programme suivant est très simple: il génère un seul point toutes les demi-secondes. S'il reçoit un SIGQUIT , il produit dix Q . S'il reçoit un SIGTSTP ( Ctrl - Z ) , il en sort dix Z . s.

S'il reçoit un SIGTSTP en imprimant des Q , il imprimera dix Z après avoir terminé avec les Q s. C’est une bonne chose.

Toutefois, s'il reçoit un SIGQUIT lors de l'impression des Z , il ne parvient pas à imprimer les Q après eux. Au lieu de cela, il les imprime seulement après que je termine manuellement l'exécution via un KeyboardInterrupt. Je souhaite que les Q soient imprimés immédiatement après les Z .

Cela se produit avec Python2.3.

Qu'est-ce que je fais mal? 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)
Était-ce utile?

La solution

Votre plus gros problème est le blocage des gestionnaires de signaux.

Ceci est généralement déconseillé car cela peut conduire à des conditions de chronométrage étranges. Mais ce n’est pas tout à fait la cause de votre problème car la condition de minutage à laquelle vous êtes vulnérable existe à cause de votre choix de gestionnaires de signaux.

Quoi qu'il en soit, voici comment minimiser au moins la condition de minutage en définissant uniquement des indicateurs dans vos gestionnaires et en laissant la boucle while principale pour effectuer le travail réel. L’explication du comportement étrange de votre code 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 la transmission des autres signaux lorsque son gestionnaire de signaux est en cours d'exécution. Lorsque le noyau va livrer SIGQUIT et constate que le gestionnaire de SIGTSTP est toujours en cours d'exécution, il l'enregistre simplement pour plus tard. Lorsqu'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 distribue maintenant.

Vous remarquerez que si vous changez pendant que 1: est pour i dans la plage (60): dans la boucle principale et refaites votre test, le programme se fermera. sans exécuter le gestionnaire SIGTSTP, car exit ne déclenche pas à nouveau le mécanisme de transmission du signal du noyau.

Bonne chance!

Autres conseils

Sous Python 2.5.2 sous Linux 2.6.24, votre code fonctionne exactement comme vous décrivez les résultats souhaités (si un signal est reçu tout en continuant de traiter un signal précédent, le nouveau signal est traité immédiatement après la fin du premier). .

Sous 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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top