Gérer le signal SIGINT en C

Jinku Hu 12 octobre 2023
  1. Utilisez la fonction signal pour enregistrer le programme de gestion des signaux SIGINT
  2. Utilisez la fonction sigaction pour enregistrer la routine de gestion des signaux SIGINT
Gérer le signal SIGINT en C

Cet article présente plusieurs méthodes pour gérer le signal SIGINT en C.

Utilisez la fonction signal pour enregistrer le programme de gestion des signaux SIGINT

SIGINT est l’un des signaux prédéfinis associés au caractère d’interruption du terminal (généralement Ctrl+C). Cela oblige le shell à arrêter le processus en cours et à revenir à sa boucle principale, affichant une nouvelle invite de commande à l’utilisateur. Notez que les signaux ne sont que de petites notifications envoyées entre les processus à l’intérieur du noyau et l’espace utilisateur. Ils sont parfois appelés interruptions logicielles car ils arrêtent généralement l’exécution normale du programme et exécutent l’action spéciale pour le type de signal donné. Les actions sont principalement définies par défaut dans les systèmes, mais l’utilisateur peut implémenter une fonction spéciale et l’enregistrer en tant que nouvelle action pour le signal.

Attention cependant, certains signaux ont un comportement strictement fixe attribué par le système d’exploitation et ne peuvent pas être remplacés, car le noyau les utilise pour faire des choses critiques comme mettre fin aux processus qui ne répondent pas.

SIGINT, cependant, est le type de signal qui peut être traité, ce qui signifie que l’utilisateur peut enregistrer une fonction personnalisée à exécuter lorsque le processus reçoit le signal. L’action par défaut du signal SIGINT est la fin du processus. Dans l’exemple de code suivant, nous implémentons un programme qui exécute une boucle infinie while, appelant la fonction fprintf en continu à l’intérieur. Bien que, juste avant le démarrage de la boucle, nous appelons la fonction signal pour enregistrer la fonction SIGINT sigintHandler en tant que gestionnaire, qui n’a qu’un seul appel de fonction, imprimant une chaîne dans stdout.

Notez que write est utilisé à la place de printf car le code du gestionnaire de signaux ne doit pas appeler des fonctions non réentrantes qui modifient les données globales du programme sous le capot. Pour illustrer l’exemple, vous devez exécuter le programme puis envoyer le signal SIGINT de l’autre terminal pour observer le comportement. Habituellement, il devrait arrêter d’exécuter la boucle while, afficher la chaîne "Caught SIGINT!" Et quitter avec le code d’état de réussite.

#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);
}

Utilisez la fonction sigaction pour enregistrer la routine de gestion des signaux SIGINT

Même si les implémentations modernes de l’appel de la fonction signal dans les systèmes UNIX fonctionnent de manière fiable pour des cas d’utilisation simples, il est recommandé d’utiliser la fonction sigaction pour enregistrer les gestionnaires de signaux. Il offre beaucoup plus d’options par rapport à l’appel signal, mais il fournit également des fonctionnalités de base qui sont nécessaires pour tout cas d’utilisation sérieux des signaux. sigaction prend des arguments spéciaux struct sigaction pour spécifier le pointeur de la fonction du gestionnaire et d’autres indicateurs avec lui. Dans ce cas, nous implémentons un scénario où un processus fils exécute une boucle while avec une variable globale shutdown_flag comme expression de condition pendant que le parent l’attend. Notez que la variable shutdown_flag est de type sig_atomic_t, un entier spécial qui peut être modifié globalement en toute sécurité à partir du code du gestionnaire de signaux. Ainsi, une fois que l’utilisateur envoie le signal SIGINT au processus fils, la fonction cleanupRoutine est appelée qui met le shutdown_flag à la valeur 0 et le contrôle retourne à la boucle while où l’expression de condition est à nouveau évaluée et le zéro l’oblige à sortir de la boucle. L’enfant sort et le parent obtient son statut au retour de la fonction 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);
}
Auteur: 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

Article connexe - C Signal