Come utilizzare i gestori di segnale in lingua C?

Come utilizzare i gestori di segnale in lingua C?
In questo articolo ti mostreremo come utilizzare i gestori di segnale in Linux usando il linguaggio C. Ma prima discuteremo di ciò che è il segnale, come genererà alcuni segnali comuni che puoi usare nel tuo programma e quindi vedremo come vari segnali possono essere gestiti da un programma mentre il programma esegue. Quindi iniziamo.

Segnale

Un segnale è un evento che viene generato per avvisare un processo o thread che è arrivata una situazione importante. Quando un processo o un thread ha ricevuto un segnale, il processo o il thread fermeranno ciò che sta facendo e intraprende un po 'di azione. Il segnale può essere utile per la comunicazione tra processo.

Segnali standard

I segnali sono definiti nel file di intestazione segnale.H Come macro costante. Il nome del segnale è iniziato con un "sig" e seguito da una breve descrizione del segnale. Quindi, ogni segnale ha un valore numerico unico. Il tuo programma dovrebbe sempre utilizzare il nome dei segnali, non il numero di segnali. Il motivo è che il numero del segnale può differire in base al sistema, ma il significato dei nomi sarà standard.

La macro Nsig è il numero totale di segnale definito. Il valore di Nsig è uno maggiore del numero totale di segnale definito (tutti i numeri del segnale sono allocati consecutivamente).

Di seguito sono riportati i segnali standard:

Nome del segnale Descrizione
Sighup Aggancia il processo. Il segnale di sospiro viene utilizzato per segnalare la disconnessione del terminale dell'utente, possibilmente perché una connessione remota viene persa o riattaccata.
Sigint Interrompere il processo. Quando l'utente digita il carattere intr (normalmente Ctrl + C) viene inviato il segnale Sigint.
SIGQUIT Lascia il processo. Quando l'utente digita il carattere di lasciare (normalmente Ctrl + \) viene inviato il segnale SIGQUIT.
Sigill Istruzione illegale. Quando viene fatto un tentativo di eseguire imbotti di immondizia o privilegiata, viene generato il segnale SIGIll. Inoltre, la sigill può essere generata quando lo stack trabocca o quando il sistema ha difficoltà a eseguire un gestore del segnale.
SigTrap Traccia trappola. Un'istruzione di breakpoint e altre istruzioni trap genereranno il segnale SigTrap. Il debugger utilizza questo segnale.
Sigabrt Abortire. Il segnale Sigabrt viene generato quando viene chiamata la funzione abort (). Questo segnale indica un errore rilevato dal programma stesso e riportato dalla chiamata di funzione Abort ().
Sigfpe Eccezione a punta mobile. Quando si è verificato un errore aritmetico fatale, viene generato il segnale SIGFPE.
Sigusr1 e Sigusr2 I segnali Sigusr1 e Sigusr2 possono essere usati come desideri. È utile scrivere un gestore del segnale per loro nel programma che riceve il segnale per una semplice comunicazione tra process.

Azione predefinita dei segnali

Ogni segnale ha un'azione predefinita, una delle seguenti:

Termine: Il processo terminerà.
Nucleo: Il processo terminerà e produrrà un file di dump principale.
IGN: Il processo ignorerà il segnale.
Fermare: Il processo si fermerà.
Cont: Il processo continuerà a essere fermato.

L'azione predefinita può essere modificata utilizzando la funzione Handler. L'azione predefinita di qualche segnale non può essere modificata. Sigkill E Sigabrt L'azione predefinita del segnale non può essere modificata o ignorata.

Gestione del segnale

Se un processo riceve un segnale, il processo ha una scelta di azione per quel tipo di segnale. Il processo può ignorare il segnale, può specificare una funzione di gestore o accettare l'azione predefinita per quel tipo di segnale.

  • Se l'azione specificata per il segnale viene ignorata, il segnale viene scartato immediatamente.
  • Il programma può registrare una funzione di gestore utilizzando la funzione come segnale O Sigazione. Questo si chiama gestore cattura il segnale.
  • Se il segnale non è stato né gestito né ignorato, ha luogo la sua azione predefinita.

Possiamo gestire il segnale usando segnale O Sigazione funzione. Qui vediamo come il più semplice segnale() La funzione viene utilizzata per la gestione dei segnali.

INT SIGNAL () (int Signum, void (*func) (int))

IL segnale() chiamerò il Func funzione se il processo riceve un segnale Signor. IL segnale() Restituisce un puntatore per funzionare Func in caso di successo o restituisce un errore a errno e -1 altrimenti.

IL Func Il puntatore può avere tre valori:

  1. Sig_dfl: È un puntatore al sistema predefinito Sig_dfl (), dichiarato in H File di intestazione. Viene utilizzato per intraprendere un'azione predefinita del segnale.
  2. Sig_ign: È un puntatore al sistema ignorare la funzione Sig_ign (),dichiarato in H File di intestazione.
  3. Puntatore della funzione gestore definita dall'utente: Il tipo di funzione gestore definito dall'utente è void (*) (int), significa che il tipo di ritorno è nullo e un argomento di tipo int.

Esempio di gestore del segnale di base

#includere
#includere
#includere
void sig_handler (int Signum)
// Il tipo di restituzione della funzione gestore dovrebbe essere nullo
printf ("\ Ninside Handler Function \ n");

int main ()
segnale (sigint, sig_handler); // Registra il gestore del segnale
per (int i = 1 ;; i ++) // Infinite Loop
printf ("%d: nella funzione principale \ n", i);
sonno (1); // ritardo per 1 secondo

restituzione 0;

Nello screenshot dell'output di Esempio1.C, possiamo vedere che nella funzione principale infinite loop sta eseguendo. Quando l'utente ha digitato CTRL+C, viene invocata l'esecuzione della funzione principale e la funzione gestore del segnale. Dopo il completamento della funzione gestore, l'esecuzione della funzione principale è ripresa. Quando il tipo di utente ha digitato CTRL+\, il processo viene lasciato.

Ignora l'esempio dei segnali

#includere
#includere
#includere
int main ()
segnale (sigint, sig_ign); // Registra il gestore del segnale per ignorare il segnale
per (int i = 1 ;; i ++) // Infinite Loop
printf ("%d: nella funzione principale \ n", i);
sonno (1); // ritardo per 1 secondo

restituzione 0;

Qui la funzione gestore è registrata a Sig_ign () funzione per ignorare l'azione del segnale. Quindi, quando l'utente ha digitato Ctrl+C, Sigint Il segnale sta generando ma l'azione viene ignorata.

Esempio di gestore di segnale Reregister

#includere
#includere
#includere
void sig_handler (int Signum)
printf ("\ Ninside Handler Function \ n");
segnale (sigint, sig_dfl); // RE Registra il gestore del segnale per l'azione predefinita

int main ()
segnale (sigint, sig_handler); // Registra il gestore del segnale
per (int i = 1 ;; i ++) // Infinite Loop
printf ("%d: nella funzione principale \ n", i);
sonno (1); // ritardo per 1 secondo

restituzione 0;

Nello screenshot dell'output di Esempio3.C, possiamo vedere che quando l'utente ha digitato la prima volta Ctrl+C, la funzione gestore invocata. Nella funzione gestore, il gestore del segnale si registra a Sig_dfl Per l'azione predefinita del segnale. Quando l'utente ha digitato Ctrl+C per la seconda volta, il processo viene terminato che è l'azione predefinita di Sigint segnale.

Invio di segnali:

Un processo può anche inviare esplicitamente segnali a se stesso o a un altro processo. La funzione di sollevare () e kill () può essere utilizzata per l'invio di segnali. Entrambe le funzioni sono dichiarate nel segnale.File di intestazione H.

int sollevare (int Signum)

La funzione di sollevamento () utilizzata per l'invio del segnale Signor al processo di chiamata (se stesso). Restituisce zero in caso di successo e un valore diverso da zero se fallisce.

int kill (pid_t pid, int Signum)

La funzione di uccisione utilizzata per inviare un segnale Signor a un processo o gruppo di processo specificato da pid.

Esempio di gestore del segnale Sigusr1

#includere
#includere
void sig_handler (int Signum)
printf ("Funzione di gestori interni \ n");

int main ()
segnale (sigusr1, sig_handler); // Registra il gestore del segnale
printf ("Inside Main Function \ n");
sollevare (Sigusr1);
printf ("Inside Main Function \ n");
restituzione 0;

Qui, il processo invia il segnale Sigusr1 a se stesso usando la funzione di alza ().

Raccolta con il programma di esempio di uccisione

#includere
#includere
#includere
void sig_handler (int Signum)
printf ("Funzione di gestori interni \ n");

int main ()
pid_t pid;
segnale (sigusr1, sig_handler); // Registra il gestore del segnale
printf ("Inside Main Function \ n");
pid = getpid (); // ID processo di se stesso
uccidere (PID, Sigusr1); // Invia Sigusr1 a se stesso
printf ("Inside Main Function \ n");
restituzione 0;

Qui, il processo invia Sigusr1 segnale a se stesso usando uccisione() funzione. getpid () è usato per ottenere l'ID processo di se stesso.

Nel prossimo esempio vedremo come comunica i processi dei genitori e dei figli uccisione() e funzione del segnale.

Comunicazione figlio genitore con segnali

#includere
#includere
#includere
#includere
void sig_handler_parent (int Signum)
printf ("genitore: ha ricevuto un segnale di risposta da figlio \ n");

void sig_handler_child (int Signum)
printf ("Child: ha ricevuto un segnale dal genitore \ n");
sonno (1);
kill (getppid (), sigusr1);

int main ()
pid_t pid;
if ((pid = fork ())<0)
printf ("fork non riuscito \ n");
uscita (1);

/ * Processo figlio */
else if (pid == 0)
segnale (sigusr1, sig_handler_child); // Registra il gestore del segnale
printf ("Child: Waiting for Signal \ n");
pausa();

/ * Processo genitore */
altro
segnale (sigusr1, sig_handler_parent); // Registra il gestore del segnale
sonno (1);
printf ("genitore: invio del segnale a figlio \ n");
uccidere (PID, Sigusr1);
printf ("genitore: in attesa di risposta \ n");
pausa();

restituzione 0;

Qui, forchetta() La funzione crea il processo figlio e restituire zero al processo figlio e dell'ID processo figlio al processo genitore. Quindi, il PID è stato verificato per decidere il processo dei genitori e del figlio. Nel processo del genitore, è dormito per 1 secondo in modo che il processo figlio possa registrare la funzione del gestore del segnale e attendere il segnale dal genitore. Dopo 1 secondo processo genitore inviare Sigusr1 segnale al processo di bambino e attendere il segnale di risposta da figlio. Nel processo figlio, prima è in attesa del segnale dal genitore e quando viene ricevuto il segnale, viene invocata la funzione del gestore. Dalla funzione gestore, il processo figlio ne invia un altro Sigusr1 segnale al genitore. Qui getppid () La funzione viene utilizzata per ottenere l'ID del processo genitore.

Conclusione

Il segnale in Linux è un grande argomento. In questo articolo abbiamo visto come gestire il segnale dal molto semplice e anche conoscere come generare il segnale, come un processo può inviare il segnale a se stesso e altro processo, come il segnale può essere utilizzato per la comunicazione tra processi.