Kako koristiti upravljače signalom na jeziku C?

How Use Signal Handlers C Language



U ovom ćemo vam članku pokazati kako koristiti rukovatelje signalom u Linuxu pomoću jezika C. No prvo ćemo raspraviti što je signal, kako će generirati neke uobičajene signale koje možete koristiti u svom programu, a zatim ćemo pogledati kako program može rukovati različitim signalima dok se program izvršava. Dakle, počnimo.

Signal

Signal je događaj koji se generira kako bi obavijestio proces ili nit da je stigla neka važna situacija. Kad proces ili nit primi signal, proces ili nit će prestati raditi i poduzeti određene radnje. Signal može biti koristan za međuprocesnu komunikaciju.







Standardni signali

Signali su definirani u datoteci zaglavlja signal.h kao makro konstanta. Naziv signala započeo je sa SIG, a nakon njega slijedi kratak opis signala. Dakle, svaki signal ima jedinstvenu numeričku vrijednost. Vaš program uvijek treba koristiti naziv signala, a ne broj signala. Razlog je to što se broj signala može razlikovati ovisno o sustavu, ali značenje imena bit će standardno.



Makronaredba NSIG je ukupni broj definiranih signala. Vrijednost NSIG je jedan veći od ukupnog broja definiranih signala (Svi brojevi signala dodjeljuju se uzastopno).



Slijede standardni signali:





Naziv signala Opis
SIGHUP Prekinite postupak. Signal SIGHUP koristi se za prijavu isključenja korisničkog terminala, vjerojatno zato što se udaljena veza izgubila ili prekinula vezu.
ZNAČAJ Prekinite proces. Kada korisnik upiše znak INTR (obično Ctrl + C), šalje se signal SIGINT.
SIGQUIT Napustite proces. Kad korisnik upiše znak QUIT (obično Ctrl + ), šalje se signal SIGQUIT.
PEČAT Nezakonita pouka. Kada se pokuša izvršiti smeće ili privilegirana instrukcija, generira se signal SIGILL. Također, SIGILL se može generirati kada se hrpa prelijeva ili kada sustav ima problema s pokretanjem rukovatelja signalom.
SIGTRAP Zamka za tragove. Naredba točke prekida i druga naredba zamke generirat će signal SIGTRAP. Debugger koristi ovaj signal.
SIGABRT Prekid. Signal SIGABRT generira se kada se pozove funkcija abort (). Ovaj signal ukazuje na pogrešku koju je program otkrio i prijavio pozivom funkcije abort ().
SIGFPE Izuzetak s pomičnim zarezom. Kad se dogodila fatalna aritmetička pogreška, generira se signal SIGFPE.
SIGUSR1 i SIGUSR2 Signali SIGUSR1 i SIGUSR2 mogu se koristiti kako želite. Za njih je korisno napisati upravljač signalom u program koji prima signal za jednostavnu međuprocesnu komunikaciju.

Zadano djelovanje signala

Svaki signal ima zadanu radnju, jednu od sljedećih:

Termin: Proces će se prekinuti.
Jezgra: Postupak će se prekinuti i proizvesti datoteku dumpa jezgre.
Paljenje: Postupak će zanemariti signal.
Stop: Proces će se zaustaviti.
Račun: Postupak će se nastaviti zaustavljanjem.



Zadana radnja može se promijeniti pomoću funkcije rukovatelja. Zadana radnja nekog signala ne može se promijeniti. SIGKILL i SIGABRT Zadana radnja signala ne može se promijeniti ili zanemariti.

Rukovanje signalom

Ako proces primi signal, proces ima izbor akcije za tu vrstu signala. Postupak može zanemariti signal, može navesti funkciju rukovatelja ili prihvatiti zadanu radnju za takvu vrstu signala.

  • Ako se navedena radnja za signal zanemari, signal se odmah odbacuje.
  • Program može registrirati funkciju rukovatelja pomoću funkcija kao što je signal ili sigakcija . To se naziva rukovatelj hvata signal.
  • Ako signal nije obrađen niti zanemaren, događa se njegova zadana radnja.

S signalom možemo upravljati pomoću signal ili sigakcija funkcija. Ovdje vidimo kako je najjednostavnije signal() funkcija se koristi za rukovanje signalima.

intsignal() (intznak, poništiti (*funkcija)(int))

The signal() će nazvati funkcija funkciju ako proces primi signal znak . The signal() vraća pokazivač na funkciju funkcija ako je uspješno ili vraća pogrešku na errno i -1 u protivnom.

The funkcija pokazivač može imati tri vrijednosti:

  1. SIG_DFL : To je pokazivač na zadanu funkciju sustava SIG_DFL () , deklarirano u h datoteku zaglavlja. Koristi se za poduzimanje zadanih radnji signala.
  2. SIG_IGN : To je pokazivač na funkciju zanemarivanja sustava SIG_IGN () , deklarirano u h datoteku zaglavlja.
  3. Korisnički definirani pokazivač na funkciju rukovatelja : Korisnički definirana vrsta funkcije rukovatelja je void (*) (int) , znači da je povratni tip void i jedan argument tipa int.

Primjer osnovnog rukovatelja signalom

#uključi
#uključi
#uključi
poništitisig_handler(intznak){

// Povratni tip funkcije rukovatelja trebao bi biti void
printf (' nUnutarnja funkcija rukovatelja n');
}

intglavni(){
signal(ZNAČAJ,sig_handler); // Registrirajte rukovatelja signalom
za(inti=1;;i++){ //Beskonačna petlja
printf ('%d: Unutar glavne funkcije n',i);
spavati(1); // Odgoda za 1 sekundu
}
povratak 0;
}

Na snimci zaslona izlaza iz Primjera 1.c možemo vidjeti da se u glavnoj funkciji izvršava beskonačna petlja. Kad je korisnik upisao Ctrl+C, izvršavanje glavne funkcije prestaje i poziva se funkcija rukovalaca signalom. Nakon dovršetka funkcije rukovatelja, izvršavanje glavne funkcije je nastavljeno. Kada je korisnik upisao Ctrl+, proces se prekida.

Primjer zanemarivanja signala

#uključi
#uključi
#uključi
intglavni(){
signal(ZNAČAJ,SIG_IGN); // Registrirajte rukovatelja signalom za ignoriranje signala

za(inti=1;;i++){ //Beskonačna petlja
printf ('%d: Unutar glavne funkcije n',i);
spavati(1); // Odgoda za 1 sekundu
}
povratak 0;
}

Ovdje je funkcija rukovatelja registrirana na SIG_IGN () funkcija za zanemarivanje djelovanja signala. Dakle, kada je korisnik upisao Ctrl+C, ZNAČAJ signal se generira, ali se radnja zanemaruje.

Ponovno registrirajte primjer upravljača signalom

#uključi
#uključi
#uključi

poništitisig_handler(intznak){
printf (' nUnutarnja funkcija rukovatelja n');
signal(ZNAČAJ,SIG_DFL); // Ponovno registrirajte rukovatelja signalom za zadanu radnju
}

intglavni(){
signal(ZNAČAJ,sig_handler); // Registrirajte rukovatelja signalom
za(inti=1;;i++){ //Beskonačna petlja
printf ('%d: Unutar glavne funkcije n',i);
spavati(1); // Odgoda za 1 sekundu
}
povratak 0;
}

Na snimci zaslona izlaza iz Primera3.c možemo vidjeti da se pri prvom upisu Ctrl+C aktivirala funkcija rukovatelja. U funkciji rukovatelja, obrađivač signala se ponovno registrira u SIG_DFL za zadano djelovanje signala. Kada je korisnik po drugi put upisao Ctrl+C, proces se prekida što je zadana radnja od ZNAČAJ signal.

Slanje signala:

Proces također može izričito slati signale sebi ili drugom procesu. raise () i kill () funkcija mogu se koristiti za slanje signala. Obje su funkcije deklarirane u datoteci zaglavlja signal.h.

int podići (intznak)

Funkcija raise () koja se koristi za slanje signala znak do procesa pozivanja (samog). Vraća nulu ako je uspješna i vrijednost različitu od nule ako ne uspije.

intubiti(pid_t pid, intznak)

Funkcija kill koja se koristi za slanje signala znak u proces ili grupu procesa koju je odredio pid .

Primjer upravljača signalom SIGUSR1

#uključi
#uključi

poništitisig_handler(intznak){
printf ('Unutarnja funkcija rukovatelja n');
}

intglavni(){
signal(SIGUSR1,sig_handler); // Registrirajte rukovatelja signalom
printf ('Unutar glavne funkcije n');
podići (SIGUSR1);
printf ('Unutar glavne funkcije n');
povratak 0;
}

Ovdje proces šalje signal SIGUSR1 sebi pomoću funkcije raise ().

Podignite s primjerom programa Kill

#uključi
#uključi
#uključi
poništitisig_handler(intznak){
printf ('Unutarnja funkcija rukovatelja n');
}

intglavni(){
pid_t pid;
signal(SIGUSR1,sig_handler); // Registrirajte rukovatelja signalom
printf ('Unutar glavne funkcije n');
pid=getpid(); // ID samog procesa
ubiti(pid,SIGUSR1); // Pošaljite SIGUSR1 sebi
printf ('Unutar glavne funkcije n');
povratak 0;
}

Ovdje proces šalje SIGUSR1 signal za sebe pomoću ubiti() funkcija. getpid () koristi se za dobivanje ID -a procesa.

U sljedećem primjeru vidjet ćemo kako roditeljski i podređeni procesi komuniciraju (međuprocesna komunikacija) koristeći ubiti() i funkciju signala.

Komunikacija roditelja s signalima

#uključi
#uključi
#uključi
#uključi
poništitisig_handler_parent(intznak){
printf ('Roditelj: Primio je signal odgovora od djeteta n');
}

poništitisig_handler_child(intznak){
printf ('Dijete: Primio signal od roditelja n');
spavati(1);
ubiti(getppid(),SIGUSR1);
}

intglavni(){
pid_t pid;
ako((pid=vilica())<0){
printf ('Fork Failed n');
Izlaz (1);
}
/ * Podređeni proces */
drugo ako(pid==0){
signal(SIGUSR1,sig_handler_child); // Registrirajte rukovatelja signalom
printf ('Dijete: čeka signal n');
pauza();
}
/ * Roditeljski proces */
drugo{
signal(SIGUSR1,sig_handler_parent); // Registrirajte rukovatelja signalom
spavati(1);
printf ('Roditelj: slanje signala djetetu n');
ubiti(pid,SIGUSR1);
printf ('Roditelj: čeka odgovor n');
pauza();
}
povratak 0;
}

Ovdje, vilica () funkcija stvara podređeni proces i vraća nulu podređenom procesu, a ID podređenog procesa roditeljskom procesu. Dakle, pid je provjeren kako bi se odlučilo o roditeljskom i podređenom procesu. U roditeljskom procesu, miruje 1 sekundu, tako da podređeni proces može registrirati funkciju rukovanja signalom i čekati signal od roditelja. Nakon 1 sekunde nadređeni proces poslati SIGUSR1 signal djetetu i pričekajte djetetov odgovor. U podređenom procesu, prvo se čeka signal od roditelja, a kada se signal primi, poziva se funkcija rukovatelja. Iz funkcije rukovatelja, podređeni proces šalje drugu SIGUSR1 signal roditelju. Ovdje getppid () funkcija se koristi za dobivanje ID -a nadređenog procesa.

Zaključak

Signal u Linuxu velika je tema. U ovom smo članku vidjeli kako upravljati signalom od samih osnovnih osnova, te stekli znanje o tome kako signal generira, kako proces može poslati signal sebi i drugim procesima, kako se signal može koristiti za međuprocesnu komunikaciju.