Domanda

Se per qualche miracolo si verifica un segfault nel nostro programma, voglio catturare SIGSEGV e far sapere all'utente (possibilmente un client GUI) con un unico codice di ritorno che si è verificato un grave problema. Allo stesso tempo, vorrei visualizzare le informazioni sulla riga di comando per mostrare quale segnale è stato catturato.

Oggi il nostro gestore di segnale ha il seguente aspetto:

void catchSignal (int reason) {
  std :: cerr << "Caught a signal: " << reason << std::endl;
  exit (1);
}

Riesco a sentire le urla dell'orrore con quanto sopra, come ho letto da questo thread che è male chiamare una funzione non rientrante da un gestore di segnali.

Esiste un modo portatile per gestire il segnale e fornire informazioni agli utenti?

MODIFICA: O almeno portatile nel framework POSIX?

È stato utile?

Soluzione

Questa table elenca tutte le funzioni che POSIX garantisce di essere asincrone-sicure per il segnale e che quindi possono essere chiamate da un gestore di segnale.

Usando il comando 'write' da questa tabella, il seguente relativamente "brutto" si spera che la soluzione farà il trucco:

#include <csignal>

#ifdef _WINDOWS_
#define _exit _Exit
#else
#include <unistd.h>
#endif

#define PRINT_SIGNAL(X) case X: \
          write (STDERR_FILENO, #X ")\n" , sizeof(#X ")\n")-1); \
          break;

void catchSignal (int reason) {
  char s[] = "Caught signal: (";
  write (STDERR_FILENO, s, sizeof(s) - 1);
  switch (reason)
  {
    // These are the handlers that we catch
    PRINT_SIGNAL(SIGUSR1);
    PRINT_SIGNAL(SIGHUP);
    PRINT_SIGNAL(SIGINT);
    PRINT_SIGNAL(SIGQUIT);
    PRINT_SIGNAL(SIGABRT);
    PRINT_SIGNAL(SIGILL);
    PRINT_SIGNAL(SIGFPE);
    PRINT_SIGNAL(SIGBUS);
    PRINT_SIGNAL(SIGSEGV);
    PRINT_SIGNAL(SIGTERM);
  }

  _Exit (1);  // 'exit' is not async-signal-safe
}

MODIFICA: Creazione su Windows.

Dopo aver provato a creare questo Windows, sembra che 'STDERR_FILENO' non sia definito. Dalla documentazione tuttavia il suo valore sembra essere "2".

#include <io.h>
#define STDIO_FILENO 2

EDIT: 'exit' non dovrebbe essere chiamato neanche dal gestore del segnale!

Come sottolineato da fizzer , chiamare _Exit in quanto sopra è un approccio a mazza per segnali come HUP e TERM. Idealmente, quando questi segnali vengono catturati con una bandiera con "volatile sig_atomic_t" type può essere utilizzato per notificare al programma principale che dovrebbe uscire.

Quanto segue ho trovato utile nelle mie ricerche.

  1. Introduzione alla programmazione dei segnali Unix
  2. Estensione dei segnali tradizionali

Altri suggerimenti

FWIW, 2 è un errore standard anche su Windows, ma avrai bisogno di una compilazione condizionale perché il loro write () si chiama _write (). Ti consigliamo anche

#ifdef SIGUSR1 /* or whatever */

ecc. intorno a tutti i riferimenti a segnali non garantiti come definiti dallo standard C.

Inoltre, come indicato sopra, non si desidera gestire SIGUSR1, SIGHUP, SIGINT, SIGQUIT e SIGTERM in questo modo.

Richard, ancora abbastanza karma per commentare, quindi temo una nuova risposta. Questi sono segnali asincroni; non hai idea di quando vengano consegnati, quindi probabilmente sarai nel codice della biblioteca che deve essere completato per rimanere coerente. I gestori di segnali per questi segnali sono pertanto tenuti a ritornare. Se chiamate exit (), la libreria eseguirà alcune operazioni post-main (), incluse le funzioni di chiamata registrate con atexit () e la pulizia dei flussi standard. Questa elaborazione potrebbe non riuscire se, ad esempio, il segnale è arrivato in una funzione I / O di libreria standard. Pertanto in C90 non è consentito chiamare exit (). Vedo ora che C99 rilassa il requisito fornendo una nuova funzione _Exit () in stdlib.h. _Exit () può essere tranquillamente chiamato da un gestore per un segnale asincrono. _Exit () non chiamerà le funzioni atexit () e potrebbe omettere di ripulire i flussi standard a discrezione dell'implementazione.

A bk1e (commentatore alcuni post in alto) Il fatto che SIGSEGV sia sincrono è il motivo per cui non è possibile utilizzare funzioni che non sono progettate per rientrare. Cosa succede se la funzione che si è arrestata in modo anomalo era in possesso di un blocco e la funzione chiamata dal gestore del segnale tenta di acquisire lo stesso blocco?

Questa è una possibilità, ma non è "il fatto che SIGSEGV sia sincrono" che sia il problema. Chiamare funzioni non rientranti dal gestore è molto peggio con segnali asincroni per due motivi:

  • gestori di segnali asincroni sono (generalmente) sperando di tornare e riprendere la normale esecuzione del programma. UN il gestore per un segnale sincrono è (generalmente) sta per terminare comunque, quindi non hai perso molto se si schianta.
  • in senso perverso, hai il controllo assoluto su quando viene inviato un segnale sincrono - succede quando esegui il tuo codice difettoso, e in nessun altro momento. Non hai alcun controllo quando viene emesso un segnale asincrono. A meno che il codice I / O dell'OP non sia se stesso la causa del difetto, ad es. emettendo un brutto carattere * - il suo messaggio di errore ha una ragionevole possibilità di successo.

Scrivi un programma di avvio per eseguire il programma e segnalare all'utente un codice di uscita anomalo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top