Gestire il segnale SIGINT in C

Jinku Hu 12 ottobre 2023
  1. Utilizzare la funzione signal per registrare la routine di gestione del segnale SIGINT
  2. Utilizzare la funzione sigaction per registrare la routine di gestione del segnale SIGINT
Gestire il segnale SIGINT in C

Questo articolo mostrerà diversi metodi su come gestire il segnale SIGINT in C.

Utilizzare la funzione signal per registrare la routine di gestione del segnale SIGINT

SIGINT è uno dei segnali predefiniti associati al carattere di interruzione del terminale (comunemente Ctrl+C). Fa sì che la shell interrompa il processo corrente e ritorni al suo bucle principale, visualizzando un nuovo prompt dei comandi per l’utente. Nota che i segnali sono solo piccole notifiche inviate tra i processi all’interno del kernel e lo spazio utente. A volte sono chiamati interrupt software poiché di solito interrompono la normale esecuzione del programma ed eseguono l’azione speciale per il tipo di segnale dato. Le azioni sono per lo più definite come predefinite nei sistemi, ma l’utente può implementare una funzione speciale e registrarla come nuova azione per il segnale.

Tuttavia, alcuni segnali hanno un comportamento strettamente fisso assegnato dal sistema operativo e non possono essere sovrascritti, poiché il kernel li usa per fare cose critiche come terminare i processi che non rispondono.

SIGINT, tuttavia, è il tipo di segnale che può essere gestito, il che significa che l’utente può registrare una funzione personalizzata da eseguire quando il processo riceve il segnale. L’azione predefinita del segnale SIGINT è che il processo termini. Nel seguente codice di esempio, implementiamo un programma che esegue un bucle infinito while, chiamando la funzione fprintf continuamente al suo interno. Sebbene, appena prima che il bucle venga avviato, chiamiamo la funzione signal per registrare la funzione SIGINT sigintHandler come gestore, che ha solo una chiamata di funzione, stampando una stringa nello stdout.

Si noti che write è usato al posto di printf perché il codice del gestore del segnale non deve chiamare funzioni non rientranti che modificano i dati del programma globale sotto il cofano. Per dimostrare l’esempio, è necessario eseguire il programma e quindi inviare il segnale SIGINT dall’altro terminale per osservare il comportamento. Di solito, dovrebbe interrompere l’esecuzione del cicli while, stampare la stringa "Caught SIGINT!" Ed uscire con il codice di stato di successo.

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

Utilizzare la funzione sigaction per registrare la routine di gestione del segnale SIGINT

Anche se le moderne implementazioni della chiamata di funzione signal nei sistemi UNIX funzionano in modo affidabile per casi d’uso semplici, si consiglia di utilizzare la funzione sigaction per registrare i gestori di segnali. Offre molte più opzioni rispetto alla chiamata signal, ma fornisce anche funzionalità di base che sono necessarie per qualsiasi caso di utilizzo serio dei segnali. sigaction accetta argomenti speciali struct sigaction per specificare il puntatore alla funzione del gestore e altri indicatori con esso. In questo caso, implementiamo uno scenario in cui un processo figlio esegue un cicli while con una variabile globale shutdown_flag come espressione di condizione mentre il genitore lo attende. Notare che la variabile shutdown_flag è di tipo sig_atomic_t, un numero intero speciale che può essere modificato in modo sicuro a livello globale dal codice del gestore del segnale. Quindi, una volta che l’utente invia il segnale SIGINT al processo figlio, viene invocata la funzione cleanupRoutine che imposta il valore shutdown_flag a 0 e il controllo ritorna al cicli while dove l’espressione della condizione viene valutata di nuovo e lo zero lo costringe a rompere dal bucle. Il bambino esce e il genitore ottiene il suo stato quando ritorna la funzione 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);
}
Autore: 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

Articolo correlato - C Signal