Com s'utilitzen gestors de senyal en llenguatge C?

How Use Signal Handlers C Language



En aquest article us mostrarem com utilitzar gestors de senyals a Linux amb llenguatge C. Però primer discutirem què és el senyal, com generarà alguns senyals comuns que podeu utilitzar al vostre programa i, a continuació, veurem com un senyal pot gestionar diversos senyals mentre s’executa el programa. Comencem, doncs.

Senyal

Un senyal és un esdeveniment que es genera per notificar a un procés o fil que ha arribat alguna situació important. Quan un procés o fil ha rebut un senyal, el procés o fil detindrà el que fa i realitzarà alguna acció. El senyal pot ser útil per a la comunicació entre processos.







Senyals estàndard

Els senyals es defineixen al fitxer de capçalera senyal.h com a constant macro. El nom del senyal ha començat amb un SIG i ha seguit d’una breu descripció del senyal. Per tant, cada senyal té un valor numèric únic. El vostre programa sempre ha d’utilitzar el nom dels senyals, no el número de senyals. La raó és que el nombre de senyals pot variar segons el sistema, però el significat dels noms serà estàndard.



La macro NSIG és el nombre total de senyal definit. El valor de NSIG és un més gran que el nombre total de senyals definits (tots els números de senyal s’assignen consecutivament).



A continuació es mostren els senyals estàndard:





Nom del senyal Descripció
MIRADA Pengeu el procés. El senyal SIGHUP s’utilitza per informar de la desconnexió del terminal de l’usuari, possiblement perquè es perd una connexió remota o es penja.
SIGINT Interrompre el procés. Quan l'usuari escriu el caràcter INTR (normalment Ctrl + C) s'envia el senyal SIGINT.
SIGQUIT Surt del procés. Quan l'usuari escriu el caràcter QUIT (normalment Ctrl + ) s'envia el senyal SIGQUIT.
SEGELL Instrucció il·legal. Quan s’intenta executar escombraries o instruccions privilegiades, es genera el senyal SIGILL. A més, SIGILL es pot generar quan la pila es desborda o quan el sistema té problemes per executar un controlador de senyal.
SIGTRAP Trampa de traça. Una instrucció de punt d’interrupció i altres instruccions de trampa generaran el senyal SIGTRAP. El depurador utilitza aquest senyal.
SIGABRT Avortar. El senyal SIGABRT es genera quan es crida la funció abort (). Aquest senyal indica un error que el propi programa detecta i que informa la trucada a la funció abort ().
SIGFPE Excepció de coma flotant. Quan es va produir un error aritmètic fatal, es genera el senyal SIGFPE.
SIGUSR1 i SIGUSR2 Els senyals SIGUSR1 i SIGUSR2 es poden utilitzar com vulgueu. És útil escriure un gestor de senyals al programa que rebi el senyal per a una comunicació senzilla entre processos.

Acció predeterminada dels senyals

Cada senyal té una acció predeterminada, una de les següents:

Termini: El procés finalitzarà.
Nucli: El procés finalitzarà i produirà un fitxer de bolcat principal.
Ign: El procés ignorarà el senyal.
Atura: El procés s’aturarà.
Compte: El procés continuarà des de la seva aturada.



L'acció predeterminada es pot canviar mitjançant la funció de controlador. L'acció predeterminada d'alguns senyals no es pot canviar. SIGKILL i SIGABRT l'acció predeterminada del senyal no es pot canviar ni ignorar.

Maneig del senyal

Si un procés rep un senyal, el procés pot triar una acció per a aquest tipus de senyal. El procés pot ignorar el senyal, especificar una funció de controlador o acceptar l'acció predeterminada per a aquest tipus de senyal.

  • Si s'ignora l'acció especificada per al senyal, el senyal es descarta immediatament.
  • El programa pot registrar una funció de control mitjançant una funció com senyal o bé sigacció . Això s’anomena un gestor que capta el senyal.
  • Si el senyal no s'ha manipulat ni ignorat, es produeix la seva acció per defecte.

Podem manejar el senyal utilitzant senyal o bé sigacció funció. Aquí veiem com és el més senzill senyal () La funció s'utilitza per tractar senyals.

intsenyal() (intsigne, buit (*funció)(int))

El senyal () trucarà al funció funció si el procés rep un senyal signe . El senyal () retorna un punter perquè funcioni funció si té èxit o torna un error a errno i -1 en cas contrari.

El funció el punter pot tenir tres valors:

  1. SIG_DFL : És un punter a la funció predeterminada del sistema SIG_DFL () , declarat a h fitxer de capçalera. S'utilitza per fer accions predeterminades del senyal.
  2. SIG_IGN : És un punter a la funció d'ignoració del sistema SIG_IGN () , declarat a h fitxer de capçalera.
  3. Punter de funció de controlador definit per l'usuari : El tipus de funció de controlador definit per l'usuari és buit (*) (int) , significa que el tipus de retorn és nul i un argument del tipus int.

Exemple de gestor de senyals bàsic

#incloure
#incloure
#incloure
buitsig_handler(intsigne){

// El tipus de retorn de la funció de controlador hauria de ser nul
printf (' nFunció de controlador interior n');
}

intprincipal(){
senyal(SIGINT,sig_handler); // Registra el gestor de senyals
per(intjo=1;;jo++){ // Bucle infinit
printf ('% d: dins de la funció principal n',jo);
dormir(1); // Retard per 1 segon
}
tornar 0;
}

A la captura de pantalla de la sortida d'Exemple1.c, podem veure que a la funció principal s'està executant un bucle infinit. Quan l’usuari ha escrit Ctrl + C, s’atura l’execució de la funció principal i s’invoca la funció de control del senyal. Després de completar la funció de controlador, es reprèn l'execució de la funció principal. Quan el tipus d'usuari ha escrit Ctrl + , el procés es tanca.

Ignora l'exemple de senyals

#incloure
#incloure
#incloure
intprincipal(){
senyal(SIGINT,SIG_IGN); // Registra el gestor de senyals per ignorar el senyal

per(intjo=1;;jo++){ // Bucle infinit
printf ('% d: dins de la funció principal n',jo);
dormir(1); // Retard per 1 segon
}
tornar 0;
}

Aquí es registra la funció de controlador SIG_IGN () funció per ignorar l'acció del senyal. Per tant, quan l’usuari va escriure Ctrl + C, SIGINT el senyal es genera però l'acció s'ignora.

Exemple de gestor de senyal de nou registre

#incloure
#incloure
#incloure

buitsig_handler(intsigne){
printf (' nFunció de controlador interior n');
senyal(SIGINT,SIG_DFL); // Torneu a registrar el gestor de senyals per accions predeterminades
}

intprincipal(){
senyal(SIGINT,sig_handler); // Registra el gestor de senyals
per(intjo=1;;jo++){ // Bucle infinit
printf ('% d: dins de la funció principal n',jo);
dormir(1); // Retard per 1 segon
}
tornar 0;
}

A la captura de pantalla de la sortida d'Exemple3.c, podem veure que quan l'usuari va escriure Ctrl + C per primera vegada, es va invocar la funció de controlador. A la funció de controlador, el gestor de senyals es registra de nou a SIG_DFL per acció predeterminada del senyal. Quan l'usuari ha escrit Ctrl + C per segona vegada, el procés s'acaba, que és l'acció predeterminada de SIGINT senyal.

Enviament de senyals:

Un procés també pot enviar explícitament senyals a si mateix o a un altre procés. La funció raise () i kill () es pot utilitzar per enviar senyals. Les dues funcions es declaren al fitxer de capçalera signal.h.

int aixecar (intsigne)

La funció raise () que s’utilitza per a l’enviament de senyals signe al procés de trucada (en si). Retorna zero si té èxit i un valor diferent de zero si falla.

intmatar(pid_t pid, intsigne)

La funció kill que s’utilitza per enviar un senyal signe a un procés o grup de processos especificat per pid .

Exemple de controlador de senyal SIGUSR1

#incloure
#incloure

buitsig_handler(intsigne){
printf ('Funció de controlador interior n');
}

intprincipal(){
senyal(SIGUSR1,sig_handler); // Registra el gestor de senyals
printf ('Dins de la funció principal n');
aixecar (SIGUSR1);
printf ('Dins de la funció principal n');
tornar 0;
}

Aquí, el procés s’envia el senyal SIGUSR1 a si mateix mitjançant la funció raise ().

Programa d’exemple Raise with Kill

#incloure
#incloure
#incloure
buitsig_handler(intsigne){
printf ('Funció de controlador interior n');
}

intprincipal(){
pid_t pid;
senyal(SIGUSR1,sig_handler); // Registra el gestor de senyals
printf ('Dins de la funció principal n');
pid=getpid(); // Identificació del procés per si mateixa
matar(pid,SIGUSR1); // Envieu SIGUSR1 a si mateix
printf ('Dins de la funció principal n');
tornar 0;
}

Aquí, s’envia el procés SIGUSR1 senyal a si mateix utilitzant matar () funció. getpid () s’utilitza per obtenir l’identificador de procés de si mateix.

En el següent exemple veurem com es comuniquen els processos de pares i fills (comunicació entre processos) mitjançant matar () i funció de senyal.

Comunicació dels pares amb senyals

#incloure
#incloure
#incloure
#incloure
buitsig_handler_parent(intsigne){
printf (Parent: ha rebut un senyal de resposta del nen n');
}

buitsig_handler_child(intsigne){
printf ('Nen: ha rebut un senyal dels pares n');
dormir(1);
matar(getppid(),SIGUSR1);
}

intprincipal(){
pid_t pid;
si((pid=forquilla())<0){
printf (Ha fallat la forquilla n');
sortir (1);
}
/ * Procés infantil * /
en cas contrari si(pid==0){
senyal(SIGUSR1,sig_handler_child); // Registra el gestor de senyals
printf ('Nen: esperant el senyal n');
pausa();
}
/ * Procés principal * /
en cas contrari{
senyal(SIGUSR1,sig_handler_parent); // Registra el gestor de senyals
dormir(1);
printf (Parent: enviament de senyal al nen n');
matar(pid,SIGUSR1);
printf (Pares: esperant resposta n');
pausa();
}
tornar 0;
}

Aquí, forquilla () La funció crea el procés fill i torna zero al procés fill i l'identificador de procés fill al procés pare. Per tant, s’ha comprovat pid per decidir el procés dels pares i fills. En el procés pare, es dorm durant 1 segon perquè el procés fill pugui registrar la funció de controlador de senyals i esperar el senyal del pare. Després d'un segon procés d'enviament principal SIGUSR1 processar el senyal al fill i esperar el senyal de resposta del nen. En el procés fill, primer s’espera el senyal del pare i, quan es rep el senyal, s’invoca la funció de controlador. Des de la funció de controlador, el procés fill n'envia un altre SIGUSR1 senyal al pare. Aquí getppid () s'utilitza per obtenir l'identificador de procés pare.

Conclusió

El senyal a Linux és un tema important. En aquest article hem vist com gestionar el senyal des del més bàsic i també obtenir coneixements sobre com es genera el senyal, com un procés pot enviar senyal a ell mateix i a un altre procés, com es pot utilitzar el senyal per a la comunicació entre processos.