Hvordan bruke signalbehandlere på C -språk?

Hvordan bruke signalbehandlere på C -språk?
I denne artikkelen skal vi vise deg hvordan du bruker signalbehandlere i Linux ved hjelp av C -språk. Men først skal vi diskutere hva som er signal, hvordan det vil generere noen vanlige signaler som du kan bruke i programmet ditt, og så vil vi se hvordan forskjellige signaler kan håndteres av et program mens programmet utfører. Så la oss starte.

Signal

Et signal er en hendelse som genereres for å varsle en prosess eller tråd om at det har kommet en viktig situasjon. Når en prosess eller tråd har mottatt et signal, vil prosessen eller tråden stoppe det den gjør og ta noen tiltak. Signal kan være nyttig for kommunikasjon mellom prosesser.

Standardsignaler

Signalene er definert i overskriftsfilen signal.h som en makrokonstant. Signalnavnet har startet med en "sig" og etterfulgt av en kort beskrivelse av signalet. Så hvert signal har en unik numerisk verdi. Programmet ditt skal alltid bruke navnet på signalene, ikke signalnummeret. Årsaken er at signalnummeret kan variere i henhold til systemet, men betydningen av navn vil være standard.

Makroen NSIG er det totale antallet signal definert. Verdien av NSIG er en større enn det totale antallet signal definert (alle signalnumre tildeles fortløpende).

Følgende er standardsignalene:

Signalnavn Beskrivelse
Sukk Hang-up prosessen. Sukksignalet brukes til å rapportere frakobling av brukerens terminal, muligens fordi en ekstern tilkobling går tapt eller henger på.
Sigint Avbryte prosessen. Når brukeren skriver inn intrekarakteren (normalt Ctrl + C) sendes SIGINT -signalet.
Sigquit Avslutt prosessen. Når brukeren skriver ut at Quit -tegnet (normalt Ctrl + \) sendes SIGQUIT -signalet.
Sigill Ulovlig instruksjon. Når det blir gjort et forsøk på å utføre søppel eller privilegert instruksjon, genereres Sigill -signalet. Sigill kan også genereres når bunken overfører, eller når systemet har problemer med å kjøre en signalbehandler.
Sigtrap Sporfelle. En instruksjon i breakpoint og annen felleinstruksjon vil generere Sigtrap -signalet. Debuggeren bruker dette signalet.
Sigabrt Avbryte. Sigabrt -signalet genereres når abort () -funksjonen kalles. Dette signalet indikerer en feil som blir oppdaget av selve programmet og rapportert av Abort () -funksjonsanropet.
Sigfpe Flytende punkt unntak. Når en dødelig aritmetisk feil oppstod, genereres SIGFPE -signalet.
SIGUSR1 og SIGUSR2 Signalene Sigusr1 og Sigusr2 kan brukes som du ønsker. Det er nyttig å skrive en signalbehandler for dem i programmet som mottar signalet for enkel kommunikasjon mellom prosessen.

Standard handling av signaler

Hvert signal har en standardhandling, ett av følgende:

Begrep: Prosessen avsluttes.
Kjerne: Prosessen vil avslutte og produsere en kjernedump -fil.
Ign: Prosessen vil ignorere signalet.
Stoppe: Prosessen vil stoppe.
Forts: Prosessen vil fortsette fra å bli stoppet.

Standardhandling kan endres ved hjelp av behandlerfunksjonen. Noen signalets standardhandling kan ikke endres. Sigkill og Sigabrt signalets standardhandling kan ikke endres eller ignorerer.

Signalhåndtering

Hvis en prosess mottar et signal, har prosessen et valg av handling for den slags signal. Prosessen kan ignorere signalet, kan spesifisere en behandlerfunksjon, eller godta standardhandlingen for den slags signal.

  • Hvis den spesifiserte handlingen for signalet blir ignorert, kastes signalet umiddelbart.
  • Programmet kan registrere en behandlerfunksjon ved hjelp av funksjon som for eksempel signal eller Sigaction. Dette kalles en behandler fanger signalet.
  • Hvis signalet ikke verken har blitt håndtert eller ignorert, finner standardaksjonen sted.

Vi kan håndtere signalet ved hjelp av signal eller Sigaction funksjon. Her ser vi hvordan de enkleste signal() Funksjon brukes til håndteringssignaler.

int signal () (int Signum, void (*func) (int))

De signal() vil ringe func funksjon hvis prosessen mottar et signal signum. De signal() Returnerer en peker for å fungere func Hvis vellykket eller den returnerer en feil til Errno og -1 ellers.

De func Peker kan ha tre verdier:

  1. SIG_DFL: Det er en peker til systemets standardfunksjon SIG_DFL (), erklært i h headerfil. Det brukes til å ta standardhandling av signalet.
  2. Sig_ign: Det er en peker for systemet ignorerer funksjonen Sig_ign (),erklært i h headerfil.
  3. Brukerdefinert behandlerfunksjonspeker: Den brukerdefinerte behandlerfunksjonstypen er tomrom (*) (int), betyr at returtypen er ugyldig og ett argument for type int.

Grunnleggende signalbehandlereksempel

#inkludere
#inkludere
#inkludere
void sig_handler (int signum)
// Returtype for behandlerfunksjonen skal være ugyldig
printf ("\ ninside Handler Function \ n");

int main ()
signal (sigint, sig_handler); // Registrer signalbehandler
for (int i = 1 ;; i ++) // uendelig sløyfe
printf ("%d: Inne hovedfunksjon \ n", i);
søvn (1); // forsinkelse i 1 sekund

retur 0;

I skjermbildet av output fra eksempel1.C, vi kan se at i hovedfunksjonen utføres uendelig sløyfe. Når brukeren skrev CTRL+C, blir hovedfunksjonens utførelsesstopp og førerfunksjonen til signalet påkalt. Etter fullføring av behandlerfunksjonen gjenopptok hovedfunksjonens utførelse. Når brukertypen skrev Ctrl+\, avsluttes prosessen.

Ignorer signaler eksempel

#inkludere
#inkludere
#inkludere
int main ()
signal (sigint, sig_ign); // Registrer signalbehandler for å ignorere signalet
for (int i = 1 ;; i ++) // uendelig sløyfe
printf ("%d: Inne hovedfunksjon \ n", i);
søvn (1); // forsinkelse i 1 sekund

retur 0;

Her er behandlerfunksjonen registrert til Sig_ign () Funksjon for å ignorere signalaksjonen. Så når brukeren skrev Ctrl+C, Sigint signalet genererer, men handlingen blir ignorert.

REGISTER SIGNAL HANDLER Eksempel

#inkludere
#inkludere
#inkludere
void sig_handler (int signum)
printf ("\ ninside Handler Function \ n");
Signal (SIGINT, SIG_DFL); // re Registrer signalbehandler for standardhandling

int main ()
signal (sigint, sig_handler); // Registrer signalbehandler
for (int i = 1 ;; i ++) // uendelig sløyfe
printf ("%d: Inne hovedfunksjon \ n", i);
søvn (1); // forsinkelse i 1 sekund

retur 0;

I skjermbildet av output fra eksempel3.C, vi kan se at når brukeren første gang skrev CTRL+C, påkalt behandlerfunksjonen. I behandlerfunksjonen registrerer signalbehandleren seg til SIG_DFL For standard handling av signalet. Når brukeren skrev inn Ctrl+C for andre gang, avsluttes prosessen som er standardhandlingen til Sigint signal.

Sende signaler:

En prosess kan også eksplisitt sende signaler til seg selv eller til en annen prosess. Raise () og Kill () -funksjonen kan brukes til å sende signaler. Begge funksjonene er erklært i signal.H headerfil.

int Raise (int Signum)

Raise () -funksjonen som brukes til å sende signal signum til anropsprosessen (seg selv). Den returnerer null hvis den lykkes og en ikke -nullverdi hvis den mislykkes.

int kill (pid_t pid, int signum)

Killfunksjonen som brukes til å sende et signal signum til en prosess- eller prosessgruppe spesifisert av PID.

SIGUSR1 signalbehandlereksempel

#inkludere
#inkludere
void sig_handler (int signum)
printf ("Inside Handler Function \ n");

int main ()
signal (SIGUSR1, SIG_HANDLER); // Registrer signalbehandler
printf ("Inside Main Function \ n");
heve (sigusr1);
printf ("Inside Main Function \ n");
retur 0;

Her sender prosessen SIGUSR1 -signal til seg selv ved hjelp av Raise () -funksjonen.

Raise with Kill Exempel -programmet

#inkludere
#inkludere
#inkludere
void sig_handler (int signum)
printf ("Inside Handler Function \ n");

int main ()
pid_t pid;
signal (SIGUSR1, SIG_HANDLER); // Registrer signalbehandler
printf ("Inside Main Function \ n");
pid = getPid (); // Prosess -ID for seg selv
Kill (Pid, Sigusr1); // send sigusr1 til seg selv
printf ("Inside Main Function \ n");
retur 0;

Her sendes prosessen Sigusr1 signal til seg selv ved bruk av drepe() funksjon. getPid () brukes til å få prosess -ID for seg selv.

I neste eksempel vil vi se hvordan foreldre og barn prosesser kommuniserer (inter prosesskommunikasjon) ved bruk av drepe() og signalfunksjon.

Foreldre barnekommunikasjon med signaler

#inkludere
#inkludere
#inkludere
#inkludere
void Sig_Handler_Parent (int Signum)
printf ("Foreldre: mottatt et responssignal fra barn \ n");

void Sig_Handler_child (int Signum)
printf ("barn: mottatt et signal fra foreldre \ n");
søvn (1);
Kill (GetPPID (), SIGUSR1);

int main ()
pid_t pid;
if ((pid = gaffel ())<0)
printf ("gaffel mislyktes \ n");
Avslutt (1);

/ * Barneprosess */
ellers hvis (pid == 0)
signal (sigusr1, sig_handler_child); // Registrer signalbehandler
printf ("barn: venter på signal \ n");
pause();

/ * Foreldreprosess */
ellers
signal (sigusr1, sig_handler_parent); // Registrer signalbehandler
søvn (1);
printf ("Foreldre: Sendesignal til barn \ n");
Kill (Pid, Sigusr1);
printf ("Parent: Waiting for Response \ n");
pause();

retur 0;

Her, gaffel() Funksjon skaper barneprosess og returnerer null til barneprosess og barneprosess -ID til overordnet prosess. Så PID har blitt sjekket for å avgjøre foreldre- og barneprosess. I overordnet prosess er den sovet i 1 sekund slik at barneprosessen kan registrere signalbehandlerfunksjon og vente på signalet fra overordnede. Etter 1 sekunders foreldreprosess sendes Sigusr1 signal til barneprosess og vent på responssignalet fra barnet. I barneprosessen venter det først på signal fra foreldre, og når signalet mottas, påkalles behandlerfunksjonen. Fra behandlerfunksjonen sender barneprosessen en annen Sigusr1 signal til foreldre. Her GetPPID () Funksjon brukes til å få foreldreprosess -ID.

Konklusjon

Signal i Linux er et stort tema. I denne artikkelen har vi sett hvordan vi skal håndtere signal fra det helt grunnleggende, og også få kunnskap om hvordan signalet genererer, hvordan en prosess kan sende signal til seg selv og annen prosess, hvordan signal kan brukes til kommunikasjon mellom prosesser.