Frage

Ich habe ein Programm, das manchmal von Zeigerarithmetik Segfaults. Ich weiß, dass dies geschieht, aber ich kann einfach nicht vor der Zeit überprüfen, um zu sehen, ob es Segfaults oder nicht - entweder ich kann „Pre-Scan“ Eingangsdaten, um zu sehen, ob es eine segfault verursacht (was unmöglich sein kann, um zu bestimmen), oder ich kann es wieder anbringen nicht Pointer-Arithmetik zu verwenden, was eine wesentlich größere Menge an Arbeit erfordern würde, oder ich kann versuchen, eine segfault zu fangen. Also meine Frage:

1) Wie in C kann ertappe ich einen segfault? Ich weiß, etwas in dem O eine segfault verursacht, aber was kann im Fall ein C-Programm zu tun, dass es ein wenig elegant zu sterben Segfaults als nur Segmentation fault?

2) Wie tragbar ist das?

Ich stelle mir das ist ein sehr unportable Verhalten, wenn Sie also einen Code Posten segfault zu fangen, mir bitte sagen, was es funktioniert auf. Ich bin auf Mac OS X, aber ich würde mein Programm arbeitet auf so viele Plattformen wie es mögen kann und ich will sehen, was meine Optionen sind.

Und keine Sorge - im Grunde alles, was ich tun möchte, ist eine benutzerfreundliche Fehlermeldung und frei, etwas malloc()ed Speicher drucken, und dann sterben. Ich plane nicht nur alle segfaults ignorieren ich und das Pflügen vor.

War es hilfreich?

Lösung

Sie haben einen Signal-Handler zu definieren. Dies wird auf Unix-Systemen erfolgt über die Funktion sigaction . Ich habe dies getan, mit dem gleichen Code auf Fedora 64- und 32-Bit und auf Sun Solaris.

Andere Tipps

Nun, SIGSEGV ist auffangbar, und dies ist POSIX, so ist es tragbar in diesem Sinne.

Bu Ich bin besorgt darüber, dass Sie scheinen zu wollen, die segfault anstatt das Problem zu beheben zu behandeln, die die segfault verursacht. Wenn ich wählen, ob sie das Betriebssystem schuldhaft gehandelt hat, oder meine eigenen Code, ich weiß, was ich wählen würde. Ich schlage vor, Sie diesen Fehler jagen, es beheben, dann einen Testfall schreiben sicherzustellen, dass es nie wieder beißt.

Sie kann die Funktion Signal einen neuen Signal-Handler für das Signal zu installieren:

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

So etwas wie den folgenden Code:

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

Die sicheren Aktionen in einem Signal-Handler sind sehr begrenzt. Es ist unsicher jede Bibliotheksfunktion nicht bekannt anrufen sein einspringenden, die beispielsweise free() und printf() wird ausgeschlossen. Best Practice ist eine Variable und zurück zu setzen, aber das macht man nicht sehr viel helfen. Es ist auch sicher Systemaufrufe wie write() zu verwenden.

Beachten Sie, dass in den beiden Backtrace hier genannten Beispiele wird die backtrace_symbols_fd() Funktion sicher sein, weil es die rohe fd direkt verwendet, sondern der Aufruf von fprintf() ist falsch und sollte durch eine Verwendung von write() ersetzt werden.

Ich glaube, Sie versuchen, ein Problem zu lösen, das nicht existiert. Mindestens Sie am falschen Ende arbeiten. Sie werden nicht in der Lage Fang ein Segmentierungsfehler, wie dieser Fehler / Ausnahme von dem O ausgelöst wird (es ist verursacht von Ihrem Programm, das O nur Fänge it).

Ich würde empfehlen Ihnen, Ihre Strategie in Bezug auf die Eingabe zu überdenken: Warum ist es unmöglich, sie zu sanieren? Das wichtigste zu tun ist, Größe Prüfung, denn dies ist die C stdlib entsprechende Funktionen. Dann natürlich müßten Sie für gültige Eingabe in Bezug auf den Inhalt zu überprüfen. Ja, dies wird wahrscheinlich in einer Menge Arbeit zur Folge haben, aber es ist der einzige Weg, ein robustes Programm zu schreiben.

EDIT: Ich bin nicht viel von einem C-Experten, wußte nicht, dass auch ein Segmentierungsfehler durch einen Signal-Handler gehandhabt werden könnte. Trotzdem denke ich, es für die oben genannten Gründe geht nicht der richtige Weg ist.

Sie müssen einen SIGSEGV Handler zur Verfügung zu stellen, diese sieht ganz anständig .

Signalverarbeitung ist (relativ) tragbare über Unix-Maschinen (einschließlich Mac und Linux). Die großen Unterschiede sind in der Ausnahme Detail, das als Argument an die Signalbehandlungsroutine übergeben wird. Sorrty, aber Sie müssen wahrscheinlich ein paar #ifdefs für das, wenn Sie vernünftige Fehlermeldungen (wie, wo und aufgrund welcher Adresse des Fehler passiert ist) drucken mögen ...

ok, hier ist ein Codefragment für Sie zu starten:

#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 ...
}

Ihre Aufgabe ist es zu:

  • überprüfen, was SIGARGS ist (OS abhängig, so verwenden Sie eine ifdef)
  • sehen Sie, wie Fehler-Adresse und PC von den Ausnahmeinformationen in sigArgs
  • extrahieren
  • Drucken vernünftige Nachricht
  • exit

in der Theorie könnte man sogar den PC in den Signal-Handler Patch (zu nach dem fehlerhaften Befehl) und fortzuzufahren. Jedoch sind typische Signal-Handler entweder exit () oder zu einem longjmp () wieder in einen sicheren Ort im Haupt.

Bezug

Es gibt ein Beispiel dafür, wie SIGSEGV zu fangen und einen Stack-Trace mit glibc des Backtrace print () hier:

wie ein Stacktrace zu erzeugen, wenn mein C ++ App abstürzt

Sie können so Ihre segfault zu fangen und zu reinigen, aber seien Sie gewarnt: Sie sollten zu viel Material nicht in einem Signal-Handler tun, vor allem Dinge, die Anrufe wie malloc () beinhalten. Es gibt eine Menge Anrufe, die nicht sicher sind, signalisieren, und Sie können sich in den Fuß zu schießen am Ende, wenn Sie machen, sagen wir, ein Anruf von malloc innerhalb malloc.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top