Gestisci il segnale SIGABRT in C++

Jinku Hu 12 ottobre 2023
  1. Usa sigaction per registrare il gestore del segnale SIGABRT
  2. Usa la variabile sig_atomic_t in Signal Handler
Gestisci il segnale SIGABRT in C++

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

Usa sigaction per registrare il gestore del segnale SIGABRT

I sistemi operativi basati su Unix supportano la funzionalità chiamata signal, un meccanismo mediante il quale un programma può inviare in modo asincrono un messaggio a un altro programma. I segnali vengono solitamente inviati dall’utente o dai processi del sistema operativo che devono interrompere il programma. Il gestore del segnale è la sezione di codice che viene chiamata se il programma riceve il segnale specificato. Ci sono alcuni segnali standard per attività come interrompere il programma, arrestare il programma e continuare il programma con i corrispondenti gestori di segnali predefiniti. Tuttavia, l’utente può sovrascrivere la maggior parte di questi gestori di segnali con funzioni definite dall’utente e sigaction è la funzione che lo registra.

Notare che SIGABRT è uno dei segnali che provoca la terminazione del programma per impostazione predefinita. Nel prossimo esempio di codice, sovrascriviamo il suo gestore di segnali e assegniamo la nostra funzione definita (cleanupRoutine) da chiamare una volta ricevuto il segnale.

All’inizio, l’oggetto di tipo struct sigaction dovrebbe essere dichiarato e inizializzato con la chiamata alla funzione memset.

Successivamente, al suo membro dati sa_handler dovrebbe essere assegnato l’indirizzo della funzione che deve essere chiamata.

Dopodiché, dobbiamo mascherare altri segnali dall’interruzione del gestore SIGABRT, e la chiamata alla funzione sigfillset ottiene ciò.

Infine, registriamo il gestore del segnale con la chiamata sigaction che accetta tre argomenti: numero del segnale, indirizzo della struct sigaction e una struttura opzionale in cui può essere salvata l’azione precedente.

In questo caso, ignoriamo il terzo argomento, ma il nullptr deve ancora essere specificato come parametro. Abbiamo definito la funzione cleanupRoutine per stampare la stringa sulla console e poi uscire dal programma per verificare facilmente la corretta esecuzione del gestore. Il resto del programma è il bucle infinito che deve essere interrotto quando l’utente invia il segnale SIGABRT. Per testare il programma, eseguilo in una finestra di terminale e invia il segnale dalla seconda finestra eseguendo il seguente comando kill -SIGABRT pid_num_of_program.

#include <csignal>
#include <cstring>
#include <iostream>

void cleanupRoutine(int signal_number) {
  write(2, "printed from cleanupRoutine\n", 28);
  _exit(EXIT_SUCCESS);
}

int main() {
  struct sigaction sigabrt_action {};
  memset(&sigabrt_action, 0, sizeof(sigabrt_action));
  sigabrt_action.sa_handler = &cleanupRoutine;

  if (sigfillset(&sigabrt_action.sa_mask) != 0) {
    perror("sigfillset");
    exit(EXIT_FAILURE);
  }
  if (sigaction(SIGABRT, &sigabrt_action, nullptr) != 0) {
    perror("sigaction SIGABRT");
    exit(EXIT_FAILURE);
  }

  int i = 0;
  while (true) {
    i += 1;
  }

  exit(EXIT_SUCCESS);
}

Usa la variabile sig_atomic_t in Signal Handler

I gestori di segnali sono tipi speciali di funzioni necessarie per avere determinate caratteristiche. Vale a dire, le uniche variabili su cui è garantito il corretto funzionamento sono le variabili atomiche. Esiste un tipo speciale sig_atomic_t, un numero intero che può essere impostato durante l’esecuzione del gestore del segnale. Questa variabile è dichiarata con la parola chiave volatile e qualsiasi modifica ad essa viene vista globalmente. L’esempio seguente mostra come includere questa variabile come condizione per l’istruzione loop.

Si noti che il gestore imposta solo la variabile da 1 a 0 e non esce dal programma se il segnale viene ricevuto. Significa che il programma cerca di continuare dal punto in cui era stato interrotto. In questo caso, l’iterazione del bucle viene riavviata e quando una volta che la condizione viene verificata come falsa, salta fuori dal bucle. In questo modo, l’utente può controllare il comportamento del programma utilizzando i segnali.

#include <csignal>
#include <cstring>
#include <iostream>

using std::cout;
using std::endl;

volatile sig_atomic_t shutdown_flag = 1;

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

int main() {
  struct sigaction sigabrt_action {};
  memset(&sigabrt_action, 0, sizeof(sigabrt_action));
  sigabrt_action.sa_handler = &cleanupRoutine;

  if (sigfillset(&sigabrt_action.sa_mask) != 0) {
    perror("sigfillset");
    exit(EXIT_FAILURE);
  }
  if (sigaction(SIGABRT, &sigabrt_action, nullptr) != 0) {
    perror("sigaction SIGABRT");
    exit(EXIT_FAILURE);
  }

  int i = 0;
  while (shutdown_flag) {
    i += 1;
  }
  cout << "Exiting ..." << endl;

  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