Señales en lenguaje C

Requisito previo: llamada al sistema de bifurcación, llamada al sistema de espera
Una señal es una interrupción generada por software que el sistema operativo envía a un proceso debido a que cuando el usuario presiona ctrl-c u otro proceso le dice algo a este proceso.
Hay un conjunto fijo de señales que se pueden enviar a un proceso. la señal se identifican con números enteros.
El número de señal tiene nombres simbólicos. Por ejemplo , SIGCHLD es el número de la señal enviada al proceso principal cuando finaliza el proceso secundario.
Ejemplos:

#define SIGHUP  1   /* Hangup the process */ 
#define SIGINT  2   /* Interrupt the process */ 
#define SIGQUIT 3   /* Quit the process */ 
#define SIGILL  4   /* Illegal instruction. */ 
#define SIGTRAP 5   /* Trace trap. */ 
#define SIGABRT 6   /* Abort. */

Estructuras OS para señales

  • Para cada proceso, el sistema operativo mantiene 2 números enteros con los bits correspondientes a un número de señal.
  • Los dos números enteros realizan un seguimiento de: señales pendientes y señales bloqueadas
  • Con números enteros de 32 bits, se pueden representar hasta 32 señales diferentes.

Ejemplo:
en el siguiente ejemplo, la señal SIGINT (= 2) está bloqueada y no hay señales pendientes.

Se envía una señal a un proceso configurando el bit correspondiente en el entero de señales pendientes para el proceso. Cada vez que el sistema operativo selecciona un proceso para ejecutarlo en un procesador, se verifican los enteros pendientes y bloqueados. Si no hay señales pendientes, el proceso se reinicia normalmente y continúa ejecutándose en su siguiente instrucción.

Si hay 1 o más señales pendientes, pero cada una está bloqueada, el proceso también se reinicia normalmente pero con las señales aún marcadas como pendientes. Si hay una o más señales pendientes y NO bloqueadas, el SO ejecuta las rutinas en el código del proceso para manejar las señales.

Controladores de señales predeterminados

Hay varias rutinas de manejo de señales predeterminadas. Cada señal está asociada con una de estas rutinas de manejo predeterminadas. Las diferentes rutinas del controlador predeterminado suelen tener una de las siguientes acciones:

  • Ig: Ignorar la señal; es decir, no hacer nada, solo regresar
  • Término: terminar el proceso
  • Cont: desbloquear un proceso detenido
  • Detener: bloquear el proceso
// CPP program to illustrate
// default Signal Handler
#include<stdio.h>
#include<signal.h>
  
int main()
{
    signal(SIGINT, handle_sigint);
    while (1)
    {
        printf(“hello world\n”);
        sleep(1);
    }
    return 0;
}

Salida: Imprime hola mundo infinitas veces. Si el usuario presiona ctrl-c para finalizar el proceso debido a la señal SIGINT enviada y su controlador predeterminado para finalizar el proceso.

hello world   
hello world         
hello world         
terminated          

Manejadores de señales definidos por el usuario

Un proceso puede reemplazar el controlador de señal predeterminado para casi todas las señales (pero no SIGKILL) por la función de controlador propia del usuario.
Una función de controlador de señales puede tener cualquier nombre, pero debe tener un tipo de retorno nulo y un parámetro int.
Ejemplo: puede elegir el nombre sigchld_handler para un controlador de señal para la señal SIGCHLD (terminación de un proceso secundario). Entonces la declaración sería:

void sigchld_handler(int sig);

Cuando se ejecuta un controlador de señal, el parámetro que se le pasa es el número de la señal. Un programador puede usar la misma función de manejador de señales para manejar varias señales. En este caso, el controlador necesitaría verificar el parámetro para ver qué señal se envió. Por otro lado, si una función de manejo de señales solo maneja una señal, no es necesario molestarse en examinar el parámetro ya que siempre será ese número de señal.

// CPP program to illustrate
// User-defined Signal Handler
#include<stdio.h>
#include<signal.h>
  
// Handler for SIGINT, caused by
// Ctrl-C at keyboard
void handle_sigint(int sig)
{
    printf("Caught signal %d\n", sig);
}
  
int main()
{
    signal(SIGINT, handle_sigint);
    while (1) ;
    return 0;
}

Producción:

^CCaught signal 2  // when user presses ctrl-c
^CCaught signal 2 

Enviando señales a través de kill()

Podemos enviar una señal usando kill() al proceso.

int kill(pid_t pid, int signal);
pid: id of destination process
signal: the type of signal to send
Return value: 0 if signal was sent successfully

Ejemplo:

pid_t iPid = getpid(); /* Process gets its id.*/
kill(iPid, SIGINT);  /* Process sends itself a  SIGINT signal   
(commits suicide?)(because of SIGINT 
signal default handler is terminate the process) */
        

Preguntas

1. ¿Cuál es la salida del siguiente programa?

#include<stdio.h>
#include<wait.h>
#include<signal.h>
int main()
{
    int stat;
    pid_t pid;
    if ((pid = fork()) == 0)
        while(1) ;
    else
    {
        kill(pid, SIGINT);
        wait(&stat);
        if (WIFSIGNALED(stat))
            psignal(WTERMSIG(stat), "Child term due to");
    }
}

Producción:

 Child term due to: Interrupt

2. ¿Cuál es la salida del siguiente programa?

#include<stdio.h>
#include<signal.h>
#include<wait.h>
int val = 10;
void handler(int sig)
{
    val += 5;
}
int main()
{
    pid_t pid;
    signal(SIGCHLD, handler);
    if ((pid = fork()) == 0)
    {
        val -= 3;
        exit(0);
    }
    waitpid(pid, NULL, 0);
    printf("val = %d\n", val);
    exit(0);
}

Producción:

val = 15 

3. Considere el siguiente código. ¿Cuál es la salida?

#include<stdio.h>
#include<wait.h>
#include<signal.h>
pid_t pid;
int counter = 0;
void handler1(int sig)
{
    counter++;
    printf("counter = %d\n", counter);
    /* Flushes the printed string to stdout */
    fflush(stdout);
    kill(pid, SIGUSR1);
}
void handler2(int sig)
{
    counter += 3;
    printf("counter = %d\n", counter);
    exit(0);
}
  
int main()
{
    pid_t p;
    int status;
    signal(SIGUSR1, handler1);
    if ((pid = fork()) == 0)
    {
        signal(SIGUSR1, handler2);
        kill(getppid(), SIGUSR1);
        while(1) ;
    }
    if ((p = wait(&status)) > 0)
    {
        counter += 4;
        printf("counter = %d\n", counter);
    }
}

Producción

counter = 1         //(parent’s handler) 
counter = 3         //(child’s handler) 
counter = 5         //(parent’s main) 

Este artículo es una contribución de Kadam Patel . Si le gusta GeeksforGeeks y le gustaría contribuir, también puede escribir un artículo usando contribuya.geeksforgeeks.org o envíe su artículo por correo a contribuya@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.

Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *