Manejar la señal SIGINT en C

Jinku Hu 12 octubre 2023
  1. Utilice la función signal para registrar la rutina del manejador de señales SIGINT
  2. Utilice la función sigaction para registrar la rutina del manejador de señales SIGINT
Manejar la señal SIGINT en C

Este artículo demostrará varios métodos sobre cómo manejar la señal SIGINT en C.

Utilice la función signal para registrar la rutina del manejador de señales SIGINT

SIGINT es una de las señales predefinidas que está asociada con el carácter de interrupción del terminal (comúnmente Ctrl+C). Hace que el shell detenga el proceso actual y regrese a su bucle principal, mostrando un nuevo símbolo del sistema al usuario. Tenga en cuenta que las señales son solo pequeñas notificaciones enviadas entre procesos dentro del kernel y el espacio de usuario. A veces se denominan interrupciones de software, ya que normalmente detienen la ejecución normal del programa y ejecutan la acción especial para el tipo de señal dado. Las acciones se definen principalmente como predeterminadas en todos los sistemas, pero el usuario puede implementar una función especial y registrarla como una nueva acción para la señal.

Sin embargo, tenga en cuenta que algunas señales tienen un comportamiento estrictamente fijo asignado desde el sistema operativo y no se pueden anular, ya que el kernel las usa para hacer cosas críticas como terminar procesos que no responden.

SIGINT, sin embargo, es el tipo de señal que se puede manejar, lo que significa que el usuario puede registrar una función personalizada que se ejecutará cuando el proceso reciba la señal. La acción predeterminada de la señal SIGINT es que el proceso termine. En el siguiente código de ejemplo, implementamos un programa que ejecuta un bucle infinito while, llamando a la función fprintf continuamente dentro de él. Aunque, justo antes de que se inicie el bucle, llamamos a la función signal para registrar la función SIGINT sigintHandler como manejador, que tiene solo una llamada de función, imprimiendo una cadena en el stdout.

Tenga en cuenta que se usa write en lugar de printf porque el código del manejador de señales no debe llamar a funciones no reentrantes que modifiquen los datos globales del programa debajo del capó. Para demostrar el ejemplo, debe ejecutar el programa y luego enviar la señal SIGINT desde el otro terminal para observar el comportamiento. Por lo general, debería dejar de ejecutar el bucle while, imprimir la cadena "Caught SIGINT!" Y salir con el código de estado de éxito.

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define errExit(msg)    \
  do {                  \
    perror(msg);        \
    exit(EXIT_FAILURE); \
  } while (0)

static void sigintHandler(int sig) {
  write(STDERR_FILENO, "Caught SIGINT!\n", 15);
}

int main(int argc, char *argv[]) {
  if (signal(SIGINT, sigintHandler) == SIG_ERR) errExit("signal SIGINT");

  while (1) {
    fprintf(stderr, "%d", 0);
    sleep(3);
  }

  exit(EXIT_SUCCESS);
}

Utilice la función sigaction para registrar la rutina del manejador de señales SIGINT

Aunque las implementaciones modernas de la llamada a la función señal en sistemas UNIX funcionan de manera confiable para casos de uso simples, se recomienda utilizar la función sigaction para registrar los manejadores de señales. Ofrece muchas más opciones en comparación con la llamada de señal, pero también proporciona funciones básicas que son necesarias para cualquier caso de uso serio de las señales. sigaction toma argumentos especiales de struct sigaction para especificar el puntero de la función del controlador y otros indicadores con él. En este caso, implementamos un escenario donde un proceso hijo ejecuta un bucle while con una variable global shutdown_flag como expresión de condición mientras el padre lo espera. Tenga en cuenta que la variable shutdown_flag es de tipo sig_atomic_t, un entero especial que se puede modificar de forma segura globalmente desde el código del manejador de señales. Entonces, una vez que el usuario envía la señal SIGINT al proceso hijo, se invoca la función cleanupRoutine que establece el valor de shutdown_flag en 0 y el control regresa al bucle while donde la expresión de condición se evalúa nuevamente y el cero lo obliga a separarse del bucle. El hijo sale y el padre obtiene su estado cuando vuelve la función waitpid.

#define _POSIX_C_SOURCE 199309
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

#define errExit(msg)    \
  do {                  \
    perror(msg);        \
    exit(EXIT_FAILURE); \
  } while (0)

volatile sig_atomic_t shutdown_flag = 1;

void cleanupRoutine(int signal_number) { shutdown_flag = 0; }

int main(void) {
  int wstatus;

  pid_t c_pid = fork();
  if (c_pid == -1) errExit("fork");

  if (c_pid == 0) {
    printf("printed from child process - %d\n", getpid());

    int count = 0;
    struct sigaction sigterm_action;
    memset(&sigterm_action, 0, sizeof(sigterm_action));
    sigterm_action.sa_handler = &cleanupRoutine;
    sigterm_action.sa_flags = 0;

    // Mask other signals from interrupting SIGINT handler
    if (sigfillset(&sigterm_action.sa_mask) != 0) errExit("sigfillset");

    // Register SIGINT handler
    if (sigaction(SIGINT, &sigterm_action, NULL) != 0) errExit("sigaction");

    while (shutdown_flag) {
      getpid();
    }
    printf("pid: %d exited\n", getpid());

    exit(EXIT_SUCCESS);
  } else {
    printf("printed from parent process - %d\n", getpid());
    int ret;

    if (waitpid(c_pid, &wstatus, WUNTRACED) == -1) errExit("waitpid");
  }

  exit(EXIT_SUCCESS);
}
Autor: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Artículo relacionado - C Signal