Cum se utilizează gestionarele de semnal în limbajul C?

How Use Signal Handlers C Language



În acest articol vă vom arăta cum să utilizați handler-uri de semnal în Linux folosind limbajul C. Dar mai întâi vom discuta ce este semnalul, cum va genera unele semnale comune pe care le puteți utiliza în programul dvs. și apoi vom analiza modul în care diferite semnale pot fi tratate de un program în timp ce programul se execută. Asadar, hai sa incepem.

Semnal

Un semnal este un eveniment care este generat pentru a notifica un proces sau un thread că a sosit o situație importantă. Când un proces sau un thread a primit un semnal, procesul sau thread-ul va opri ceea ce face și va acționa. Semnalul poate fi util pentru comunicarea între procese.







Semnale standard

Semnalele sunt definite în fișierul antet semnal.h ca o constantă macro. Numele semnalului a început cu un SIG și a fost urmat de o scurtă descriere a semnalului. Deci, fiecare semnal are o valoare numerică unică. Programul dvs. ar trebui să utilizeze întotdeauna numele semnalelor, nu numărul semnalelor. Motivul este că numărul semnalului poate diferi în funcție de sistem, dar semnificația numelor va fi standard.



Macro NSIG este numărul total de semnal definit. Valoarea a NSIG este cu unul mai mare decât numărul total de semnal definit (toate numerele de semnal sunt alocate consecutiv).



Următoarele sunt semnalele standard:





Numele semnalului Descriere
PREZENTARE Închideți procesul. Semnalul SIGHUP este utilizat pentru a raporta deconectarea terminalului utilizatorului, posibil pentru că o conexiune la distanță se pierde sau închide.
SIGINT Întrerupeți procesul. Când utilizatorul tastează caracterul INTR (în mod normal Ctrl + C) se trimite semnalul SIGINT.
SIGQUIT Părăsiți procesul. Când utilizatorul tastează caracterul QUIT (în mod normal Ctrl + ) se trimite semnalul SIGQUIT.
SIGILIU Instrucțiune ilegală. Când se încearcă executarea gunoiului sau a instrucțiunilor privilegiate, se generează semnalul SIGILL. De asemenea, SIGILL poate fi generat atunci când stiva se revarsă sau când sistemul are probleme la executarea unui handler de semnal.
SIGTRAP Urmărește capcana. O instrucțiune de punct de întrerupere și alte instrucțiuni de captare vor genera semnalul SIGTRAP. Depanatorul folosește acest semnal.
SIGABRT Abortează. Semnalul SIGABRT este generat la apelarea funcției abort (). Acest semnal indică o eroare care este detectată de programul însuși și raportată de apelul funcției abort ().
SIGFPE Excepție în virgulă mobilă. Când a apărut o eroare aritmetică fatală, se generează semnalul SIGFPE.
SIGUSR1 și SIGUSR2 Semnalele SIGUSR1 și SIGUSR2 pot fi utilizate după cum doriți. Este util să scrieți un handler de semnal pentru ei în programul care primește semnalul pentru o comunicare simplă între procese.

Acțiune implicită a semnalelor

Fiecare semnal are o acțiune implicită, una dintre următoarele:

Termen: Procesul se va încheia.
Nucleu: Procesul se va încheia și va produce un fișier de dump de bază.
Ign: Procesul va ignora semnalul.
Stop: Procesul se va opri.
Cont: Procesul va continua să fie oprit.



Acțiunea implicită poate fi modificată utilizând funcția de gestionare. Acțiunea implicită a unui semnal nu poate fi modificată. SIGKILL și SIGABRT acțiunea implicită a semnalului nu poate fi modificată sau ignorată.

Manipularea semnalului

Dacă un proces primește un semnal, procesul poate alege acțiunea pentru acest tip de semnal. Procesul poate ignora semnalul, poate specifica o funcție de gestionare sau poate accepta acțiunea implicită pentru acel tip de semnal.

  • Dacă acțiunea specificată pentru semnal este ignorată, atunci semnalul este eliminat imediat.
  • Programul poate înregistra o funcție de manipulare utilizând funcții precum semnal sau sigaction . Acest lucru se numește un handler captează semnalul.
  • Dacă semnalul nu a fost nici manipulat, nici ignorat, acțiunea sa implicită are loc.

Putem gestiona semnalul folosind semnal sau sigaction funcţie. Aici vedem cum cel mai simplu semnal() funcția este utilizată pentru gestionarea semnalelor.

intsemnal() (intsemn, nul (*func)(int))

The semnal() va apela la func funcție dacă procesul primește un semnal semn . The semnal() returnează un pointer pentru a funcționa func dacă are succes sau returnează o eroare la errno și -1 în caz contrar.

The func indicatorul poate avea trei valori:

  1. SIG_DFL : Este un indicator către funcția implicită a sistemului SIG_DFL () , declarat în h fișier antet. Este folosit pentru acționarea implicită a semnalului.
  2. SIG_IGN : Este un indicator către funcția de ignorare a sistemului SIG_IGN () , declarat în h fișier antet.
  3. Pointerul funcției handler definite de utilizator : Tipul funcției de manipulare definite de utilizator este nul (*) (int) , înseamnă că tipul return este nul și un argument de tip int.

Exemplu de gestionare a semnalului de bază

#include
#include
#include
nulsig_handler(intsemn){

// Tipul de returnare a funcției de gestionare ar trebui să fie nul
printf (' nFuncția de manipulare în interior n');
}

intprincipal(){
semnal(SIGINT,sig_handler); // Înregistrați gestionarul de semnal
pentru(inteu=1;;eu++){ //Buclă infinită
printf ('% d: În interiorul funcției principale n',eu);
dormi(1); // Întârziere pentru 1 secundă
}
întoarcere 0;
}

În captura de ecran a rezultatului Exemplului1.c, putem vedea că în funcția principală se execută o buclă infinită. Când utilizatorul a tastat Ctrl + C, funcția principală se oprește și funcția de gestionare a semnalului este invocată. După finalizarea funcției de gestionare, executarea funcției principale a fost reluată. Când tipul de utilizator a tastat Ctrl + , procesul este închis.

Ignorați semnalele Exemplu

#include
#include
#include
intprincipal(){
semnal(SIGINT,SIG_IGN); // Înregistrați gestionarul de semnal pentru a ignora semnalul

pentru(inteu=1;;eu++){ //Buclă infinită
printf ('% d: În interiorul funcției principale n',eu);
dormi(1); // Întârziere pentru 1 secundă
}
întoarcere 0;
}

Aici funcția de gestionare este înregistrată la SIG_IGN () funcție pentru ignorarea acțiunii semnalului. Deci, când utilizatorul a tastat Ctrl + C, SIGINT semnalul generează, dar acțiunea este ignorată.

Exemplu de gestionare a semnalului de înregistrare

#include
#include
#include

nulsig_handler(intsemn){
printf (' nFuncția de manipulare în interior n');
semnal(SIGINT,SIG_DFL); // Re Înregistrați gestionarul de semnal pentru acțiune implicită
}

intprincipal(){
semnal(SIGINT,sig_handler); // Înregistrați gestionarul de semnal
pentru(inteu=1;;eu++){ //Buclă infinită
printf ('% d: În interiorul funcției principale n',eu);
dormi(1); // Întârziere pentru 1 secundă
}
întoarcere 0;
}

În captura de ecran a ieșirii din Example3.c, putem vedea că atunci când utilizatorul a tastat pentru prima dată Ctrl + C, funcția de manipulare a fost invocată. În funcția de handler, handler-ul semnalului se înregistrează la SIG_DFL pentru acțiunea implicită a semnalului. Când utilizatorul a tastat Ctrl + C pentru a doua oară, procesul este încheiat, care este acțiunea implicită a SIGINT semnal.

Trimiterea semnalelor:

Un proces poate, de asemenea, să transmită în mod explicit semnale către sine sau către alt proces. funcția de ridicare () și kill () poate fi utilizată pentru trimiterea de semnale. Ambele funcții sunt declarate în fișierul header signal.h.

int a ridica (intsemn)

Funcția de ridicare () utilizată pentru trimiterea semnalului semn la procesul de apelare (în sine). Întoarce zero dacă are succes și o valoare diferită de zero dacă eșuează.

intucide(pid_t pid, intsemn)

Funcția de ucidere utilizată pentru trimiterea unui semnal semn la un proces sau grup de procese specificat de pid .

Exemplu de manipulare a semnalului SIGUSR1

#include
#include

nulsig_handler(intsemn){
printf („Funcția de manipulare în interior n');
}

intprincipal(){
semnal(SIGUSR1,sig_handler); // Înregistrați gestionarul de semnal
printf („În interiorul funcției principale n');
a ridica (SIGUSR1);
printf („În interiorul funcției principale n');
întoarcere 0;
}

Aici, procesul trimite semnalul SIGUSR1 către sine folosind funcția de ridicare ().

Ridicați cu Kill Exemplu de program

#include
#include
#include
nulsig_handler(intsemn){
printf („Funcția de manipulare în interior n');
}

intprincipal(){
pid_t pid;
semnal(SIGUSR1,sig_handler); // Înregistrați gestionarul de semnal
printf („În interiorul funcției principale n');
pid=obraznic(); // ID-ul procesului de sine
ucide(pid,SIGUSR1); // Trimiteți SIGUSR1 către sine
printf („În interiorul funcției principale n');
întoarcere 0;
}

Aici, procesul trimite SIGUSR1 semnal pentru sine folosind ucide() funcţie. getpid () este folosit pentru a obține ID-ul procesului.

În exemplul următor vom vedea cum comunică procesele părinte și copil (comunicare între procese) folosind ucide() și funcția de semnal.

Comunicarea copilului părinte cu semnale

#include
#include
#include
#include
nulsig_handler_parent(intsemn){
printf („Părinte: a primit un semnal de răspuns de la copil n');
}

nulsig_handler_child(intsemn){
printf („Copil: a primit un semnal de la părinte n');
dormi(1);
ucide(înfiorat(),SIGUSR1);
}

intprincipal(){
pid_t pid;
dacă((pid=furculiţă())<0){
printf ('Furca a eșuat n');
Ieșire (1);
}
/ * Proces copil * /
altceva dacă(pid==0){
semnal(SIGUSR1,sig_handler_child); // Înregistrați gestionarul de semnal
printf („Copil: așteaptă semnalul n');
pauză();
}
/ * Procesul părinte * /
altceva{
semnal(SIGUSR1,sig_handler_parent); // Înregistrați gestionarul de semnal
dormi(1);
printf („Părinte: trimiterea semnalului către copil n');
ucide(pid,SIGUSR1);
printf („Părinte: așteaptă răspunsul n');
pauză();
}
întoarcere 0;
}

Aici, furculiţă() funcția creează procesul copil și returnează zero la procesul copil și ID-ul procesului copil procesului părinte. Deci, pid a fost verificat pentru a decide procesul părintelui și copilului. În procesul părinte, este dormit timp de 1 secundă, astfel încât procesul copil să poată înregistra funcția de gestionare a semnalului și să aștepte semnalul de la părinte. După 1 secundă procesul părinte trimite SIGUSR1 semnalul către copil procesează și așteptați semnalul de răspuns de la copil. În procesul copil, mai întâi se așteaptă semnalul de la părinte și când este primit semnalul, este invocată funcția de gestionare. Din funcția de gestionare, procesul copil trimite altul SIGUSR1 semnal către părinte. Aici getppid () funcția este utilizată pentru obținerea ID-ului procesului părinte.

Concluzie

Semnalul în Linux este un subiect important. În acest articol am văzut cum să gestionăm semnalul din elementele de bază și, de asemenea, să obținem cunoștințe despre modul în care generează semnalul, modul în care un proces poate trimite semnal către el însuși și alt proces, modul în care semnalul poate fi utilizat pentru comunicarea inter-proces.