Hur använder man signalhanterare på C -språk?

How Use Signal Handlers C Language



I den här artikeln kommer vi att visa dig hur du använder signalhanterare i Linux med C -språk. Men först kommer vi att diskutera vad som är signal, hur det kommer att generera några vanliga signaler som du kan använda i ditt program och sedan ska vi titta på hur olika signaler kan hanteras av ett program medan programmet körs. Så, låt oss börja.

Signal

En signal är en händelse som genereras för att meddela en process eller tråd att någon viktig situation har kommit. När en process eller tråd har mottagit en signal, kommer processen eller tråden att stoppa vad den gör och vidta åtgärder. Signal kan vara användbar för kommunikation mellan processer.







Standardsignaler

Signalerna definieras i rubrikfilen signal.h som en makrokonstant. Signalnamnet har börjat med ett SIG och följt av en kort beskrivning av signalen. Så varje signal har ett unikt numeriskt värde. Ditt program bör alltid använda namnet på signalerna, inte signalernas nummer. Orsaken är att signalnummer kan variera beroende på system men namns betydelse är standard.



Makrot NSIG är det totala antalet signaler som definieras. Värdet av NSIG är en större än det totala antalet definierade signalerna (Alla signalnummer tilldelas i följd).



Följande är standardsignalerna:





Signalnamn Beskrivning
SIGHUP Lägg på processen. SIGHUP -signalen används för att rapportera frånkoppling av användarens terminal, möjligen för att en fjärranslutning förloras eller läggs på.
TECKEN Avbryt processen. När användaren skriver INTR -tecknet (normalt Ctrl + C) skickas SIGINT -signalen.
SIGQUIT Avsluta processen. När användaren skriver QUIT -tecknet (normalt Ctrl + ) skickas SIGQUIT -signalen.
SIGILL Olaglig instruktion. När ett försök görs att utföra skräp eller privilegierad instruktion genereras SIGILL -signalen. SIGILL kan också genereras när stacken flyter över eller när systemet har problem med att köra en signalhanterare.
SIGTRAP Spårfälla. En brytpunktsinstruktion och annan fällinstruktion genererar SIGTRAP -signalen. Felsökaren använder denna signal.
SIGABRT Avbryta. SIGABRT -signalen genereras när funktionen abort () anropas. Denna signal indikerar ett fel som upptäcks av själva programmet och rapporteras av avbryt () -samtalet.
SIGFPE Flytande undantag. När ett dödligt aritmetiskt fel inträffade genereras SIGFPE -signalen.
SIGUSR1 och SIGUSR2 Signalerna SIGUSR1 och SIGUSR2 kan användas som du vill. Det är användbart att skriva en signalhanterare för dem i programmet som tar emot signalen för enkel kommunikation mellan processer.

Standardåtgärd för signaler

Varje signal har en standardåtgärd, en av följande:

Termin: Processen kommer att avslutas.
Kärna: Processen avslutas och producerar en kärndumpfil.
Ign: Processen kommer att ignorera signalen.
Sluta: Processen kommer att sluta.
Konto: Processen kommer att fortsätta från att stoppas.



Standardåtgärd kan ändras med hanteringsfunktionen. Vissa signalers standardåtgärd kan inte ändras. SIGKILL och SIGABRT signalens standardåtgärd kan inte ändras eller ignoreras.

Signalhantering

Om en process tar emot en signal har processen ett val av åtgärd för den typen av signal. Processen kan ignorera signalen, kan specificera en hanteringsfunktion eller acceptera standardåtgärden för den typen av signal.

  • Om den angivna åtgärden för signalen ignoreras, kasseras signalen omedelbart.
  • Programmet kan registrera en hanteringsfunktion med hjälp av funktion som signal eller signering . Detta kallas en hanterare fångar signalen.
  • Om signalen varken har hanterats eller ignorerats, sker dess standardåtgärd.

Vi kan hantera signalen med signal eller signering fungera. Här ser vi hur det enklaste signal() funktionen används för att hantera signaler.

intsignal() (intskylt, tomhet (*fungera)(int))

De signal() kommer att ringa fungera funktion om processen tar emot en signal skylt . De signal() returnerar en pekare till funktion fungera om det lyckas eller det returnerar ett fel till errno och -1 annars.

De fungera pekaren kan ha tre värden:

  1. SIG_DFL : Det är en pekare till systemets standardfunktion SIG_DFL () , förklarade i h rubrikfil. Den används för att vidta standardåtgärder för signalen.
  2. SIG_IGN : Det är en pekare till system ignorera funktion SIG_IGN() , förklarade i h rubrikfil.
  3. Användardefinierad hanterarfunktionspekare : Den användardefinierade hanterarfunktionstypen är void (*) (int) , betyder att returtyp är ogiltig och ett argument av typen int.

Grundläggande exempel på signalhanterare

#omfatta
#omfatta
#omfatta
tomhetsig_handler(intskylt){

// Returtyp för hanteringsfunktionen bör vara ogiltig
printf (' nFunktion inuti hanteraren n');
}

inthuvud(){
signal(TECKEN,sig_handler); // Registrera signalhanteraren
för(inti=1;;i++){ //Oändlig loop
printf ('%d: Inuti huvudfunktionen n',i);
sömn(1); // Fördröjning i 1 sekund
}
lämna tillbaka 0;
}

I skärmdumpen av utdata från Exempel1.c kan vi se att i huvudfunktionen körs oändlig slinga. När användaren har skrivit Ctrl+C, stoppas huvudfunktionens körning och signalens hanteringsfunktion. Efter avslutad hanteringsfunktion återupptogs utförandet av huvudfunktionen. När användartypen skrev Ctrl+, avslutas processen.

Ignorera exempel på signaler

#omfatta
#omfatta
#omfatta
inthuvud(){
signal(TECKEN,SIG_IGN); // Registrera signalhanteraren för att ignorera signalen

för(inti=1;;i++){ //Oändlig loop
printf ('%d: Inuti huvudfunktionen n',i);
sömn(1); // Fördröjning i 1 sekund
}
lämna tillbaka 0;
}

Här är hanteringsfunktionen registrerad till SIG_IGN() funktion för att ignorera signalåtgärden. Så när användaren skrev Ctrl+C, TECKEN signalen genererar men åtgärden ignoreras.

Registrera om Signal Handler Exempel

#omfatta
#omfatta
#omfatta

tomhetsig_handler(intskylt){
printf (' nFunktion inuti hanteraren n');
signal(TECKEN,SIG_DFL); // Registrera signalhanteraren för standardåtgärd
}

inthuvud(){
signal(TECKEN,sig_handler); // Registrera signalhanteraren
för(inti=1;;i++){ //Oändlig loop
printf ('%d: Inuti huvudfunktionen n',i);
sömn(1); // Fördröjning i 1 sekund
}
lämna tillbaka 0;
}

I skärmdumpen av utdata från Exempel3.c kan vi se att när användaren först skrev Ctrl+C, aktiverades hanteringsfunktionen. I hanteringsfunktionen registrerar signalhanteraren sig om till SIG_DFL för signalens standardåtgärd. När användaren skrev Ctrl+C för andra gången avslutas processen, vilket är standardåtgärden för TECKEN signal.

Skicka signaler:

En process kan också uttryckligen skicka signaler till sig själv eller till en annan process. raise () och kill () -funktionen kan användas för att skicka signaler. Båda funktionerna deklareras i signal.h -huvudfilen.

int höja (intskylt)

Förhöjningsfunktionen () som används för att skicka signal skylt till kallprocessen (sig själv). Det returnerar noll om det lyckas och ett värde utan noll om det misslyckas.

intdöda(pid_t pid, intskylt)

Dödsfunktionen som används för att skicka en signal skylt till en process eller processgrupp som anges av pid .

SIGUSR1 Signal Handler Exempel

#omfatta
#omfatta

tomhetsig_handler(intskylt){
printf ('Funktion inuti hanteraren n');
}

inthuvud(){
signal(SIGUSR1,sig_handler); // Registrera signalhanteraren
printf ('Inuti huvudfunktionen n');
höja (SIGUSR1);
printf ('Inuti huvudfunktionen n');
lämna tillbaka 0;
}

Här skickar processen SIGUSR1 -signal till sig själv med hjälp av höjning () -funktionen.

Höj med programmet Kill Exempel

#omfatta
#omfatta
#omfatta
tomhetsig_handler(intskylt){
printf ('Funktion inuti hanteraren n');
}

inthuvud(){
pid_t pid;
signal(SIGUSR1,sig_handler); // Registrera signalhanteraren
printf ('Inuti huvudfunktionen n');
pid=bli ledsen(); // Process -ID för sig själv
döda(pid,SIGUSR1); // Skicka SIGUSR1 till sig själv
printf ('Inuti huvudfunktionen n');
lämna tillbaka 0;
}

Här skickar processen SIGUSR1 signal till sig själv med döda() fungera. getpid () används för att få själva process -ID: t.

I nästa exempel kommer vi att se hur föräldrar och barn processer kommunicerar (Inter Process Communication) med döda() och signalfunktion.

Föräldrars barnkommunikation med signaler

#omfatta
#omfatta
#omfatta
#omfatta
tomhetsig_handler_parent(intskylt){
printf ('Förälder: Fick en svarsignal från barn n');
}

tomhetsig_handler_child(intskylt){
printf ('Barn: Fick en signal från föräldern n');
sömn(1);
döda(bli upprörd(),SIGUSR1);
}

inthuvud(){
pid_t pid;
om((pid=gaffel())<0){
printf ('Gaffeln misslyckades n');
utgång (1);
}
/ * Barnprocess */
annan om(pid==0){
signal(SIGUSR1,sig_handler_child); // Registrera signalhanteraren
printf ('Barn: väntar på signal n');
paus();
}
/ * Föräldraprocess */
annan{
signal(SIGUSR1,sig_handler_parent); // Registrera signalhanteraren
sömn(1);
printf ('Förälder: sänder signal till barn n');
döda(pid,SIGUSR1);
printf ('Förälder: väntar på svar n');
paus();
}
lämna tillbaka 0;
}

Här, gaffel() funktion skapar barnprocess och returnerar noll till barnprocess och barnprocess -ID till förälderprocess. Så, pid har kontrollerats för att avgöra föräldrarnas och barnets process. I förälderprocessen sover den i 1 sekund så att barnprocessen kan registrera signalhanteringsfunktionen och vänta på signalen från föräldern. Efter 1 sekund överordnad process skicka SIGUSR1 signal till barnprocess och vänta på svarsignal från barn. I barnprocessen väntar den först på signal från föräldern och när signalen tas emot aktiveras hanteringsfunktionen. Från hanteringsfunktionen skickar barnprocessen en annan SIGUSR1 signal till föräldern. Här getppid () -funktionen används för att få överordnad process -ID.

Slutsats

Signal i Linux är ett stort ämne. I den här artikeln har vi sett hur man hanterar signal från det grundläggande, och får också kunskap om hur signalen genererar, hur en process kan skicka signal till sig själv och andra processer, hur signalen kan användas för kommunikation mellan processer.