termA non si blocca. Sta solo visualizzando la richiamata nella casella di input. Basta premere Enter
tasto per continuare con l'input.
Prima di spiegare il tuo problema, un po' di contesto su come read
comando funziona. Legge i dati di input da stdin
fino al EOF
si incontra. È sicuro pronunciare la chiamata a read
Il comando non è bloccante quando si tratta di leggere da file su disco. Ma quando stdin
è connesso al terminale, il comando si bloccherà finché l'utente non digita qualcosa.
Come funzionano i gestori di segnali?
Una semplice spiegazione su come funziona la gestione del segnale. Vedi lo snippet sottostante in C
che agisce solo su SIGINT
(ovvero CTRL+C
)
#include <stdio.h>
#include <signal.h>
/* signal handler definition */
void signal_handler(int signum){
printf("Hello World!\n");
}
int main(){
//Handle SIGINT with a signal handler
signal(SIGINT, signal_handler);
//loop forever!
while(1);
}
Registrerà il gestore del segnale e quindi entrerà nel ciclo infinito. Quando raggiungiamo Ctrl-C
, siamo tutti d'accordo sul fatto che il gestore del segnale signal_handler()
dovrebbe essere eseguito e "Hello World!"
stampa sullo schermo, ma il programma era in un ciclo infinito. Per stampare "Hello World!"
deve essere stato il caso in cui ha interrotto il loop per eseguire il gestore del segnale, giusto? Quindi dovrebbe uscire dal ciclo così come dal programma. Vediamo:
gcc -Wall -o sighdl.o signal.c
./sighdl.o
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
Come indica l'output, ogni volta che abbiamo emesso Ctrl-C
, "Hello World!"
stampa, ma il programma ritorna al ciclo infinito. È solo dopo aver emesso un SIGQUIT
segnale con Ctrl-\
il programma è effettivamente terminato. A seconda del tuo ulimit
impostazioni, scaricherà un core o stamperà il numero di segnale ricevuto.
Sebbene l'interpretazione secondo cui il ciclo sarebbe terminato sia ragionevole, non considera il motivo principale per la gestione del segnale, ovvero la gestione asincrona degli eventi. Ciò significa che il gestore del segnale agisce al di fuori del flusso standard del controllo del programma; infatti, l'intero programma viene salvato all'interno di un contesto e viene creato un nuovo contesto solo per l'esecuzione del gestore del segnale. Una volta che il gestore del segnale ha completato le sue azioni, il contesto viene ripristinato e inizia il normale flusso di esecuzione (ovvero il while(1)
).
Per rispondere alle tue domande,
La conclusione ha verificato che quando bash esegue un comando esterno in primo piano, non gestisce alcun segnale ricevuto fino al termine del processo in primo piano
La cosa fondamentale da notare qui è l'esterno parte di comando. Nel primo caso, dove sleep
è un processo esterno ma nel secondo caso, read
è un built-in dalla shell stessa. Quindi la propagazione del segnale a questi due differisce in entrambi i casi
type read
read is a shell builtin
type sleep
sleep is /usr/bin/sleep
1.Apri un terminale, chiamato termA , ed esegui il file creato callback.sh con /bin/bash callback.sh per la prima volta, le informazioni vengono visualizzate all'istante.
Sì, questo comportamento è previsto. Perché in questo momento solo la funzione è definita e il gestore trap è registrato nella funzione myCallback
e il segnale non è ancora ricevuto nello script. Durante la sequenza di esecuzione, il messaggio dal read
prompt viene lanciato per la prima volta.
2.Apri un nuovo terminale, chiamato termB ed esegui
pkill -USR1 -f callback.sh
la prima volta, le informazioni vengono visualizzate immediatamente in termA
Sì, mentre il read
il comando è in attesa di una stringa seguita da Invio tasto premuto che segnala il EOF
, riceve un segnale SIGUSR1
dall'altro terminale, viene salvato il contesto di esecuzione corrente e il controllo viene commutato sul gestore di segnale che stampa la stringa con la data corrente.
Non appena il gestore termina l'esecuzione, il contesto riprende al while
ciclo in cui il read
il comando è ancora in attesa di una stringa di input. Fino al read
il comando ha esito positivo, tutte le trappole di segnale successive stamperanno semplicemente la stringa all'interno del gestore di segnale.
Continua in termB, esegui
pkill -USR1 -f callback.sh
la seconda volta.
Come spiegato in precedenza, il read
il comando non è completo per una volta nel tuo ciclo while, solo se riesce a leggere una stringa, la successiva iterazione del ciclo inizierà e verrà lanciato un nuovo messaggio di richiesta.
Fonte immagine:The Linux Programming Interface di Michael KerrisK
La conclusione ha verificato che quando bash esegue un comando esterno in primo piano, non gestisce alcun segnale ricevuto fino al termine del processo in primo piano.
bash
fa gestire i segnali a C
/ livello di implementazione, ma non eseguirà i gestori impostati con trap
finché il processo in primo piano non è terminato. Questo è richiesto dallo standard:
When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.
Si noti che lo standard non fa differenza tra builtin e comandi esterni; nel caso di una "utilità" come read
, che non viene eseguito in un processo separato, non è ovvio se un segnale si verifica nel suo "contesto" o in quello della shell principale, e se un gestore impostato con trap
dovrebbe essere eseguito prima o dopo read
ritorna.
problema 1:callback.sh contiene un ciclo while infinito, come spiegarlo non gestisce alcun segnale ricevuto fino al termine del processo in primo piano., in questo caso il processo in primo piano non termina mai.
È sbagliato. bash
non sta eseguendo il while
ciclo in un processo separato. Poiché non esiste un processo in primo piano bash
è in attesa, può eseguire qualsiasi gestore impostato con trap
subito. Se read
fosse un comando esterno, quello e non while
sarebbe il processo in primo piano.
issue2:Nessun
please input something for foo: shown
nel termine A;vai a termB, esegui
pkill -USR1 -f callback.sh
la terza volta.Le seguenti informazioni mostrate nuovamente in termA:
callback function called at Mon Nov 19 09:07:24 HKT 2018
Ancora nessun
please input something for foo:
mostrato in termA.Perché le informazioniplease input something for foo:
congelare?
Non gela. Basta premere Invio e verrà visualizzato di nuovo.
È semplicemente questo
a) bash
riavvierà il read
incorporato sul posto quando viene interrotto da un segnale -- non tornerà e non passerà di nuovo attraverso il while
loop
b) non visualizzerà nuovamente il prompt impostato con -p
in tal caso.
Nota che bash
non mostrerà nemmeno di nuovo il prompt nel caso in cui un SIGINT
dalla tastiera è stata gestita, anche se scarta la stringa letta finora dall'utente:
$ cat goo
trap 'echo INT' INT
echo $$
read -p 'enter something: ' var
echo "you entered '$var'"
$ bash goo
24035
enter something: foo<Ctrl-C>^CINT
<Ctrl-C>^CINT
<Ctrl-C>^CINT
bar<Enter>
you entered 'bar'
Verrà stampato you entered 'foobar'
se il segnale è stato inviato da un'altra finestra con kill -INT <pid>
invece che con Ctrl-C dal terminale.
Tutte le cose in quest'ultima parte (come read
è interrotto, ecc.) è molto bash
specifico. In altre shell come ksh
o dash
e anche in bash
quando eseguito in modalità POSIX (bash --posix
), qualsiasi segnale gestito interromperà effettivamente il read
integrato. Nell'esempio precedente, la shell stamperà you entered ''
ed esci dopo il primo ^C, e se il read
viene richiamato da un ciclo, il ciclo verrà riavviato.