Kako uporabljati upravljalnike signalov v jeziku C?

How Use Signal Handlers C Language



V tem članku vam bomo pokazali, kako uporabljati upravljalnike signalov v Linuxu z uporabo jezika C. Najprej pa se bomo pogovorili o tem, kaj je signal, kako bo ustvaril nekaj skupnih signalov, ki jih lahko uporabite v svojem programu, nato pa bomo pogledali, kako lahko program obravnava različne signale med izvajanjem programa. Torej, začnimo.

Signal

Signal je dogodek, ki se generira, da obvesti proces ali nit, da je prišlo do neke pomembne situacije. Ko proces ali nit prejme signal, bo postopek ali nit ustavil svoje početje in izvedel nekaj dejanj. Signal je lahko uporaben za medprocesno komunikacijo.







Standardni signali

Signali so definirani v datoteki z glavo signal.h kot makro konstanta. Ime signala se je začelo s SIG, nato pa kratek opis signala. Torej ima vsak signal edinstveno številčno vrednost. Vaš program mora vedno uporabljati ime signalov, ne številko signalov. Razlog je v tem, da se lahko številka signala razlikuje glede na sistem, vendar bo pomen imen standarden.



Makro NSIG je skupno število definiranih signalov. Vrednost NSIG je ena večja od skupnega števila definiranih signalov (Vse številke signalov so dodeljene zaporedno).



Sledijo standardni signali:





Ime signala Opis
SIGHUP Prekinite postopek. Signal SIGHUP se uporablja za poročanje o prekinitvi povezave uporabniškega terminala, morda zato, ker se oddaljena povezava prekine ali prekine.
PODPIS Prekinite postopek. Ko uporabnik vnese znak INTR (običajno Ctrl + C), se pošlje signal SIGINT.
SIGQUIT Zapustite postopek. Ko uporabnik vnese znak QUIT (običajno Ctrl + ), se pošlje signal SIGQUIT.
TESNILO Nezakonit pouk. Ko poskusite izvesti smeti ali privilegirana navodila, se generira signal SIGILL. SIGILL lahko ustvarite tudi, ko se sklad prepolni ali če ima sistem težave pri izvajanju upravljavca signalov.
SIGTRAP Past za sledenje. Ukaz prelomne točke in drugi ukaz pasti bosta ustvarila signal SIGTRAP. Odpravljalec napak uporablja ta signal.
SIGABRT Prekini. Signal SIGABRT se generira, ko se pokliče funkcija abort (). Ta signal označuje napako, ki jo zazna sam program in jo sporoči klic funkcije abort ().
SIGFPE Izjema s plavajočo vejico. Ko pride do usodne aritmetične napake, se generira signal SIGFPE.
SIGUSR1 in SIGUSR2 Signala SIGUSR1 in SIGUSR2 lahko uporabljate po želji. V program, ki sprejema signal za preprosto medprocesno komunikacijo, jim je koristno napisati upravljalnik signalov.

Privzeto dejanje signalov

Vsak signal ima privzeto dejanje, eno od naslednjih:

Izraz: Postopek se bo končal.
Jedro: Postopek se bo končal in ustvarila datoteko izpisa jedra.
Vžig: Postopek ne upošteva signala.
Ustavi: Postopek se bo ustavil.
Račun: Postopek se bo ustavil.



Privzeto dejanje lahko spremenite s funkcijo rokovanja. Privzetega dejanja nekaterih signalov ni mogoče spremeniti. SIGKILL in SIGABRT privzetega dejanja signala ni mogoče spremeniti ali prezreti.

Ravnanje s signalom

Če proces sprejme signal, ima postopek izbiro ukrepanja za to vrsto signala. Proces lahko prezre signal, lahko poda funkcijo vodnika ali sprejme privzeto dejanje za to vrsto signala.

  • Če zanemarite določeno dejanje za signal, se signal takoj zavrže.
  • Program lahko registrira funkcijo vodnika z uporabo funkcije, kot je npr signal ali sigaction . To se imenuje upravljavec, ki ujame signal.
  • Če signal ni bil obravnavan ali prezrt, se izvede njegovo privzeto dejanje.

Signal lahko obravnavamo z uporabo signal ali sigaction funkcijo. Tu vidimo, kako najpreprostejši signal () funkcija se uporablja za obdelavo signalov.

intsignal() (intpodpisati, nično (*funkcijo)(int))

The signal () bo poklical funkcijo funkcijo, če proces prejme signal podpisati . The signal () vrne kazalec na funkcijo funkcijo če je uspešen ali vrne napako na errno in -1 drugače.

The funkcijo kazalec ima lahko tri vrednosti:

  1. SIG_DFL : To je kazalec na privzeto funkcijo sistema SIG_DFL () , deklarirano leta h naslovno datoteko. Uporablja se za privzeto dejanje signala.
  2. SIG_IGN : To je kazalec na funkcijo prezrtega sistema SIG_IGN () , deklarirano leta h naslovno datoteko.
  3. Kazalec na funkcijo upravljavca, ki ga definira uporabnik : Uporabniško določena vrsta funkcije vodnika je void (*) (int) , pomeni, da je vrsta vrnitve nična in en argument tipa int.

Primer osnovnega upravljalca signalov

#vključi
#vključi
#vključi
ničnosig_handler(intpodpisati){

// Vrnilna vrsta funkcije vodnika mora biti nična
printf (' nFunkcija notranjega vodnika n');
}

intglavni(){
signal(PODPIS,sig_handler); // Registriraj upravljavec signala
za(intjaz=1;;jaz++){ // Neskončna zanka
printf ('%d: znotraj glavne funkcije n',jaz);
spi(1); // Zamuda za 1 sekundo
}
vrnitev 0;
}

Na posnetku zaslona iz izida Primer1.c lahko vidimo, da se v glavni funkciji izvaja neskončna zanka. Ko uporabnik vnese Ctrl+C, se izvajanje glavne funkcije ustavi in ​​prikliče se funkcija vodnika signala. Po zaključku funkcije vodnika se je izvajanje glavne funkcije nadaljevalo. Ko uporabnik vnese Ctrl+, se postopek zapre.

Primer prezrtih signalov

#vključi
#vključi
#vključi
intglavni(){
signal(PODPIS,SIG_IGN); // Registrirajte upravljavec signala za ignoriranje signala

za(intjaz=1;;jaz++){ // Neskončna zanka
printf ('%d: znotraj glavne funkcije n',jaz);
spi(1); // Zamuda za 1 sekundo
}
vrnitev 0;
}

Tukaj je funkcija vodnika registrirana v SIG_IGN () funkcijo za ignoriranje delovanja signala. Torej, ko je uporabnik vnesel Ctrl+C, PODPIS signal se generira, dejanje pa se prezre.

Znova registrirajte primer upravljalca signalov

#vključi
#vključi
#vključi

ničnosig_handler(intpodpisati){
printf (' nFunkcija notranjega vodnika n');
signal(PODPIS,SIG_DFL); // Ponovno registrirajte upravljavec signala za privzeto dejanje
}

intglavni(){
signal(PODPIS,sig_handler); // Registriraj upravljavec signala
za(intjaz=1;;jaz++){ // Neskončna zanka
printf ('%d: znotraj glavne funkcije n',jaz);
spi(1); // Zamuda za 1 sekundo
}
vrnitev 0;
}

Na posnetku zaslona iz izida Primer3.c lahko vidimo, da se je pri prvem vnosu uporabnika Ctrl+C priklicala funkcija vodnika. V funkciji upravljavca se upravljavec signala ponovno registrira v SIG_DFL za privzeto delovanje signala. Ko uporabnik drugič vnese Ctrl+C, se postopek zaključi, kar je privzeto dejanje PODPIS signal.

Pošiljanje signalov:

Proces lahko tudi izrecno pošilja signale sebi ali drugemu procesu. za pošiljanje signalov lahko uporabite funkcijo raise () in kill (). Obe funkciji sta deklarirani v datoteki glave signal.h.

int dvigniti (intpodpisati)

Funkcija dviga (), ki se uporablja za pošiljanje signala podpisati klicnemu procesu (samemu). Če je uspešen, vrne ničlo, če pa ne, nič.

intubiti(pid_t pid, intpodpisati)

Funkcija kill, ki se uporablja za pošiljanje signala podpisati v proces ali skupino procesov, ki jo določa pid .

Primer upravljalnika signalov SIGUSR1

#vključi
#vključi

ničnosig_handler(intpodpisati){
printf ('Funkcija notranjega vodnika n');
}

intglavni(){
signal(SIGUSR1,sig_handler); // Registriraj upravljavec signala
printf ('Znotraj glavne funkcije n');
dvigniti (SIGUSR1);
printf ('Znotraj glavne funkcije n');
vrnitev 0;
}

Tu proces pošlje signal SIGUSR1 k sebi s pomočjo funkcije raise ().

Dvignite s primerom programa Kill

#vključi
#vključi
#vključi
ničnosig_handler(intpodpisati){
printf ('Funkcija notranjega vodnika n');
}

intglavni(){
pid_t pid;
signal(SIGUSR1,sig_handler); // Registriraj upravljavec signala
printf ('Znotraj glavne funkcije n');
pid=getpid(); // ID procesa sam
ubiti(pid,SIGUSR1); // Pošlji SIGUSR1 sebi
printf ('Znotraj glavne funkcije n');
vrnitev 0;
}

Tukaj se pošlje postopek SIGUSR1 signal do sebe z uporabo ubiti () funkcijo. getpid () se uporablja za pridobivanje ID -ja procesa.

V naslednjem primeru bomo videli, kako starševski in podrejeni proces komunicira (Inter Process Communication) z uporabo ubiti () in funkcijo signala.

Komunikacija staršev s signali

#vključi
#vključi
#vključi
#vključi
ničnosig_handler_parent(intpodpisati){
printf („Starš: prejel odzivni signal od otroka n');
}

ničnosig_handler_child(intpodpisati){
printf ('Otrok: prejel signal od staršev n');
spi(1);
ubiti(getppid(),SIGUSR1);
}

intglavni(){
pid_t pid;
če((pid=vilice())<0){
printf ('Fork Failed n');
izhod (1);
}
/ * Otroški proces */
drugače če(pid==0){
signal(SIGUSR1,sig_handler_child); // Registriraj upravljavec signala
printf ('Otrok: čaka na signal n');
pavza();
}
/ * Starševski postopek */
drugače{
signal(SIGUSR1,sig_handler_parent); // Registriraj upravljavec signala
spi(1);
printf („Starš: pošiljanje signala otroku n');
ubiti(pid,SIGUSR1);
printf ('Starš: čaka na odgovor n');
pavza();
}
vrnitev 0;
}

Tukaj, vilica () funkcija ustvari podrejeni proces in vrne ničlo v podrejeni proces in ID podrejenega procesa v nadrejeni proces. Torej je bil pid preverjen, da bi se odločil za proces staršev in otrok. V nadrejenem procesu je spal 1 sekundo, tako da lahko podrejeni proces registrira funkcijo vodnika signala in počaka na signal starša. Po 1 sekundi nadrejenega procesa pošljite SIGUSR1 signal otroku in počakajte na otrokov odzivni signal. V podrejenem procesu najprej počaka na signal starša, po prejemu signala pa se prikliče funkcija vodnika. Iz funkcije vodnika podrejeni proces pošlje drugega SIGUSR1 signal staršem. Tukaj getppid () funkcija se uporablja za pridobivanje ID -ja nadrejenega procesa.

Zaključek

Signal v Linuxu je velika tema. V tem članku smo videli, kako ravnati s signalom od samega začetka, pa tudi spoznati, kako signal generira, kako lahko proces pošlje signal sebi in drugim procesom, kako se signal lahko uporabi za medprocesno komunikacijo.