2008-09-20 16 views
9

El siguiente programa es muy simple: genera un solo punto cada medio segundo. Si recibe un SIGQUIT, procede a la salida 0 Q s. Si recieves un SIGTSTP(Ctrl - Z), se da salida a diez Z s.Problemas con la señal de Python: el controlador SIGQUIT ¿retrasa la ejecución si se recibe SIGQUIT durante la ejecución de otro manejador de señal?

Si recieves un SIGTSTP durante la impresión Q s, se imprimirá diez Z s después de que se ha hecho con los diez Q s. Ésto es una cosa buena.

Sin embargo, si un recieves SIGQUIT durante la impresión Z s, que no se imprime Q s después de ellos. En cambio, los imprime solo después de que finalizo la ejecución manualmente a través de KeyboardInterrupt. Quiero que se imprima el Q s inmediatamente después del Z s.

Esto ocurre con Python2.3.

¿Qué estoy haciendo 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) 

Respuesta

6

Su problema más grande es el bloqueo en los controladores de señal.

Esto generalmente se desaconseja, ya que puede ocasionar condiciones de temporización extrañas. Pero no es exactamente la causa de su problema ya que la condición de tiempo que usted es vulnerable existe debido a su elección de manejadores de señal.

De todos modos, aquí le mostramos cómo minimizar al mínimo las condiciones de tiempo configurando solo banderas en sus manejadores y dejando el bucle while principal para hacer el trabajo real. La explicación de por qué su código se comporta de manera extraña se describe después del 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 todos modos, esto es lo que está pasando.

SIGTSTP es más especial que SIGQUIT.

SIGTSTP enmascara las otras señales que se entregarán mientras se está ejecutando su controlador de señal. Cuando el núcleo va a entregar SIGQUIT y ve que el controlador de SIGTSTP aún se está ejecutando, simplemente lo guarda para más adelante. Una vez que llega otra señal para la entrega, como SIGINT cuando CTRL + C (alias KeyboardInterrupt), el kernel recuerda que nunca entregó SIGQUIT y lo entrega ahora.

Notará que si cambia while 1: a en el bucle principal y vuelve a hacer su prueba, el programa saldrá sin ejecutar el controlador SIGTSTP ya que la salida no vuelve a activar el mecanismo de entrega de señal del kernel.

¡Buena suerte!

1

En Python 2.5.2 en Linux 2.6.24, su código funciona exactamente como usted describe los resultados deseados (si se recibe una señal mientras se está procesando una señal anterior, la nueva señal se procesa inmediatamente después de que termine la primera).

En Python 2.4.4 en Linux 2.6.16, veo el comportamiento problemático que describes.

No sé si esto se debe a un cambio en Python o en el kernel de Linux.

Cuestiones relacionadas