GNU/Linux >> Linux Esercitazione >  >> Linux

Quando viene gestito un segnale e perché alcune informazioni si bloccano?

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 informazioni please 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.


Linux
  1. Quando e perché dovrei usare Apt-get Update?

  2. Quando e perché usare Docker

  3. Chi utilizza i segnali POSIX in tempo reale e perché?

  4. Perché questa pipeline di shell termina?

  5. Perché clang genera testo incomprensibile quando viene reindirizzato?

Perché OpenStack segnala il tipo Hypervisor come QEMU quando libvirt_type è KVM?

Perché alcune Emoji in bianco e nero e altre sono troppo grandi?

Quando è utile setsid() o perché abbiamo bisogno di raggruppare i processi in Linux?

Perché stdout necessita di uno svuotamento esplicito quando viene reindirizzato al file?

Perché malloc() chiama mmap() e brk() in modo intercambiabile?

Perché rsync non utilizza il trasferimento delta per i file locali?