Il problema di Python è un problema: il gestore SIGQUIT ritarda l'esecuzione se SIGQUIT ha ricevuto durante l'esecuzione di un altro gestore del segnale?

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

  •  02-07-2019
  •  | 
  •  

Domanda

Il seguente programma è molto semplice: emette un singolo punto ogni mezzo secondo. Se riceve un SIGQUIT , procede alla produzione di dieci Q . Se riceve un SIGTSTP ( Ctrl - Z ) , genera dieci Z s.

Se riceve un SIGTSTP durante la stampa di Q , stamperà dieci Z dopo aver terminato con le dieci Q s. Questa è una buona cosa.

Tuttavia, se riceve un SIGQUIT durante la stampa di Z , non riesce a stampare Q dopo di loro. Invece, li stampa solo dopo aver terminato manualmente l'esecuzione tramite un KeyboardInterrupt. Voglio che le Q vengano stampate immediatamente dopo le Z .

Questo succede usando Python2.3.

Cosa sto facendo di sbagliato? 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)
È stato utile?

Soluzione

Il tuo problema più grande è il blocco dei gestori di segnale.

Questo di solito è scoraggiato poiché può portare a strane condizioni di tempo. Ma non è proprio la causa del tuo problema poiché la condizione di temporizzazione a cui sei vulnerabile esiste a causa della tua scelta di gestori di segnale.

In ogni caso, ecco come minimizzare almeno le condizioni di temporizzazione impostando solo flag nei gestori e lasciando il ciclo while principale per eseguire il lavoro effettivo. La spiegazione del perché il codice si comporta in modo strano è descritta dopo il codice.

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

Comunque, ecco cosa sta succedendo.

SIGTSTP è più speciale di SIGQUIT.

SIGTSTP maschera la trasmissione degli altri segnali mentre il suo gestore di segnale è in esecuzione. Quando il kernel consegna SIGQUIT e vede che il gestore di SIGTSTP è ancora in esecuzione, lo salva semplicemente per dopo. Quando arriva un altro segnale per la consegna, come SIGINT quando CTRL + C (aka KeyboardInterrupt), il kernel ricorda che non ha mai consegnato SIGQUIT e lo consegna ora.

Noterai che se cambi mentre 1: in per i nell'intervallo (60): nel loop principale e ripeti il ??tuo test case, il programma uscirà senza eseguire il gestore SIGTSTP poiché exit non riattiva il meccanismo di consegna del segnale del kernel.

Buona fortuna!

Altri suggerimenti

Su Python 2.5.2 su Linux 2.6.24, il tuo codice funziona esattamente come descrivi i risultati desiderati (se viene ricevuto un segnale mentre si sta ancora elaborando un segnale precedente, il nuovo segnale viene elaborato immediatamente dopo che il primo è terminato) .

Su Python 2.4.4 su Linux 2.6.16, vedo il comportamento del problema che descrivi.

Non so se ciò sia dovuto a una modifica in Python o nel kernel Linux.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top