Domanda

Ho un programma che segfaults da aritmetica dei puntatori a volte. So che questo accada, ma non posso comodamente verificare prima del tempo per vedere se segfaults o no - o posso dati di input "pre-scansione" per vedere se causerà un segfault (che può essere impossibile determinare), o posso rimontarlo non utilizzare per l'aritmetica dei puntatori, che richiederebbe una quantità significativamente maggiore di lavoro, o posso cercare di catturare un segfault. Quindi la mia domanda:

1) Come, in C, posso prendere un segfault? So qualcosa nel sistema operativo provoca un segfault, ma cosa può fare un programma di C fare nel caso in cui segfaults a morire un po 'più di un semplice garbo Segmentation fault?

2) Come Portable è questo?

Immagino che questo è un comportamento altamente portabile, quindi se si registra alcun codice per prendere un segfault, mi dica quello che funziona su. Sono su Mac OS X, ma vorrei il mio programma per lavorare su più piattaforme come può e voglio vedere quali sono le mie opzioni.

E non si preoccupi - praticamente tutto quello che voglio fare è stampare un messaggio di errore più user-friendly e di connessione della memoria malloc()ed, e poi morire. Non ho intenzione di solo ignorando tutti segfaults che ricevo e l'aratura avanti.

È stato utile?

Soluzione

È necessario definire un gestore di segnale. Questo viene fatto su sistemi Unix utilizzando la funzione di sigaction . Ho fatto questo con lo stesso codice su Fedora 64 e 32-bit, e su Sun Solaris.

Altri suggerimenti

Bene, SIGSEGV è intercettabile, e questo è POSIX, quindi è portatile in questo senso.

Bu io sono preoccupato del fatto che lei sembra voler gestire la segfault piuttosto che risolvere il problema che causa il segfault. Se dovessi scegliere se fosse il sistema operativo in colpa, o il mio codice, so che avrei scelto. Vi suggerisco di caccia ai bug che, risolvere il problema, quindi scrivere un banco di prova per assicurarsi che non ti morde di nuovo.

È possibile utilizzare la funzione di segnale per installare un nuovo gestore di segnale per il segnale:

   #include <signal.h>
   void (*signal(int signum, void (*sighandler)(int)))(int);

Qualcosa come il seguente codice:

signal(SIGINT , clean_exit_on_sig);
signal(SIGABRT , clean_exit_on_sig);
signal(SIGILL , clean_exit_on_sig);
signal(SIGFPE , clean_exit_on_sig);
signal(SIGSEGV, clean_exit_on_sig); // <-- this one is for segmentation fault
signal(SIGTERM , clean_exit_on_sig);

void 
clean_exit_on_sig(int sig_num)
{
        printf ("\n Signal %d received",sig_num);
}

Le azioni sicure in un gestore di segnale sono molto limitata. E 'pericoloso per chiamare qualsiasi funzione di libreria non è noto per essere rientrante, che escludere, ad esempio, free() e printf(). Le migliori pratiche è quello di impostare una variabile e ritorno, ma questo non ti aiuta molto. E 'anche sicuro da usare chiamate di sistema, come write().

Si noti che nei due esempi Backtrace qui indicate, la funzione backtrace_symbols_fd() sarà sicuro perché utilizza il grezzo fd direttamente, ma la chiamata a fprintf() è corretto, e deve essere sostituito da un uso di write().

Credo che si sta cercando di risolvere un problema che non esiste. Almeno si sta lavorando dalla parte sbagliata. Non sarà in grado di cattura un errore di segmentazione, come questo errore / eccezione viene generata dal sistema operativo (che è ha causato dal programma, il sistema operativo solo le catture di esso).

vorrei consigliare a ripensare la vostra strategia per quanto riguarda l'ingresso: Perché è impossibile per sanificare esso? La più importante da fare è il controllo del formato, per questo il C stdlib ha funzioni appropriate. Poi, naturalmente, dovreste verificare la presenza di input valido per quanto riguarda il contenuto. Sì, questo probabilmente porterà ad un sacco di lavoro, ma è l'unico modo per scrivere un robusto programma.

EDIT: Io non sono molto di un esperto di C, non sapeva che anche un errore di segmentazione potrebbe essere gestito da un gestore di segnale. Eppure, penso che non è il modo giusto per andare per i motivi di cui sopra.

È necessario fornire un gestore SIGSEGV, Questo sembra abbastanza decente .

gestione del segnale è portatile (relativamente) tra macchine unix (questo include Mac e Linux). Le grandi differenze sono nel dettaglio eccezione, che viene passato come argomento alla routine di gestione del segnale. Sorrty, ma probabilmente avrete bisogno di un po 'di #ifdef per questo, se si desidera stampare i messaggi di errore più ragionevoli (ad esempio, dove e grazie a quale indirizzo è verificato il guasto) ...

ok, qui è un frammento di codice per iniziare con:

#include <signal.h>

/* reached when a segv occurrs */
void
SEGVFunction( SIGARGS )
{
     ...
}

...
main(...) {
    signal(SIGSEGV, SEGVFunction); /* tell the OS, where to go in case... */
    ...
    ... do your work ...
}

Il vostro compito è quello di:

  • verificare quale SIGARGS è (dipende dal sistema operativo, in modo da utilizzare un ifdef)
  • vedere come estrarre fault-indirizzo e pc dalle informazioni eccezione nella sigArgs
  • Stampa ragionevole messaggio
  • exit

in teoria, si potrebbe anche correggere il pc nel gestore del segnale (per dopo l'istruzione fagliazione), e procedere. Tuttavia, i gestori tipici segnali sia uscita () o ad un longjmp () di nuovo in un luogo sicuro nel principale.

Per quanto riguarda

C'è un esempio di come catturare SIGSEGV e stampare una traccia dello stack utilizzando backtrace di glibc () qui:

come generare uno stacktrace quando il mio C ++ app crash

È possibile utilizzare questo per prendere il segfault e pulire, ma attenzione: non si dovrebbe fare troppa roba in un gestore di segnale, soprattutto le cose che coinvolgono nell'esecuzione di chiamate come malloc (). Ci sono un sacco di chiamate che non sono segnale sicura, e si può finire per le riprese in un piede se si fanno, per esempio, una chiamata a malloc dall'interno malloc.

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