Problemas de la señal de Python:¿El controlador SIGQUIT retrasa la ejecución si SIGQUIT se recibió durante la ejecución de otro controlador de señales?

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

  •  02-07-2019
  •  | 
  •  

Pregunta

El siguiente programa es muy sencillo:emite un solo punto cada medio segundo.Si recibe un SIGQUITAR, procede a generar diez qs.Si recibe un SIGTSTP (Control-z), produce diez zs.

Si recibe un SIGTSTP mientras imprime qs, imprimirá diez zs después de que haya terminado con los diez qs.Ésto es una cosa buena.

Sin embargo, si recibe un SIGQUITAR mientras imprime zs, no se puede imprimir qs detrás de ellos.En cambio, los imprime solo después de que finalizo manualmente la ejecución mediante KeyboardInterrupt.Quiero el qse imprimirá inmediatamente después del zs.

Esto sucede usando 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)
¿Fue útil?

Solución

Su mayor problema es el bloqueo de los manejadores de señales.

Por lo general, no se recomienda esto, ya que puede generar condiciones de tiempo extrañas.Pero no es exactamente la causa de su problema, ya que la condición de sincronización a la que es vulnerable existe debido a su elección de controladores de señal.

De todos modos, aquí se explica cómo al menos minimizar la condición de sincronización configurando solo indicadores en sus controladores y dejando que el bucle while principal haga 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 la entrega de otras señales mientras se ejecuta su controlador de señales.Cuando el kernel va a entregar SIGQUIT y ve que el controlador de SIGTSTP todavía se está ejecutando, simplemente lo guarda para más adelante.Una vez que llega otra señal para la entrega, como SIGINT cuando CONTROL+C (también conocido como KeyboardInterrupt), el kernel recuerda que nunca entregó SIGQUIT y lo entrega ahora.

Lo notarás si cambias while 1: a for i in range(60): en el bucle principal y realice su caso de prueba nuevamente, el programa saldrá sin ejecutar el controlador SIGTSTP ya que la salida no vuelve a activar el mecanismo de entrega de señales del kernel.

¡Buena suerte!

Otros consejos

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 aún se procesa una señal anterior, la nueva señal se procesa inmediatamente después de que finaliza la primera).

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

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top