Behandlung von Segmentierungsfehlern

Lesezeit: 7 Minuten

Benutzeravatar von user1225606
Benutzer1225606

Ich habe eine Anwendung, die ich verwende, um Segmentierungsfehler oder Strg-C abzufangen. Mit dem folgenden Code kann ich den Segmentierungsfehler abfangen, aber der Handler wird immer wieder aufgerufen. Wie kann ich sie aufhalten. Zu Ihrer Information, ich möchte meine Bewerbung nicht beenden. Ich kann mich nur darum kümmern, alle beschädigten Puffer freizugeben.

Ist es möglich?

void SignalInit(void )
{

struct sigaction sigIntHandler;

sigIntHandler.sa_handler = mysighandler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
sigaction(SIGSEGV, &sigIntHandler, NULL);

}

und Handler geht so.

void mysighandler()
{
MyfreeBuffers(); /*related to my applciation*/
}

Hier wird der Handler für das Segmentierungsfehlersignal mehrmals aufgerufen, und offensichtlich gibt MyfreeBuffers() mir Fehler zum Freigeben von bereits freigegebenem Speicher. Ich möchte nur einmal freigeben, aber die Anwendung trotzdem nicht beenden.

Bitte helfen Sie.

Benutzeravatar von Pavan Manjunath
Pavan Manjunath

Die Standardaktion für Dinge wie SIGSEGV ist, Ihren Prozess zu beenden, aber da Sie einen Handler dafür installiert haben, ruft er Ihren Handler auf und überschreibt das Standardverhalten. Das Problem ist jedoch, dass die Segfaulting-Anweisung möglicherweise wiederholt wird, nachdem Ihr Handler fertig ist, und wenn Sie keine Maßnahmen ergriffen haben, um den ersten Segmentfehler zu beheben, wird die wiederholte Anweisung erneut fehlerhaft und es geht weiter und weiter.

Suchen Sie also zuerst die Anweisung, die daraus resultierte SIGSEGV und versuchen Sie es zu beheben (Sie können so etwas nennen wie backtrace() im Handler und sehen Sie selbst, was schief gelaufen ist)

Außerdem sagt der POSIX-Standard, dass

Das Verhalten eines Prozesses ist undefiniert, nachdem er normal von einer signalfangenden Funktion für a zurückkehrt [XSI] SIGBUS-, SIGFPE-, SIGILL- oder SIGSEGV-Signal, das nicht von kill() generiert wurde, [RTS] sigqueue() oder raise().

Das Beste, was Sie tun können, ist also, Ihren Segfault überhaupt erst zu beheben. Der Handler für segfault soll die zugrunde liegende Fehlerbedingung nicht umgehen

Der beste Vorschlag wäre also Nicht fangen SIGSEGV. Lassen Sie es Kern entleeren. Analysiere den Kern. Korrigieren Sie die ungültige Speicherreferenz und los geht’s!

  • Das Abfangen von Segmentierungsverletzungen ist manchmal nützlich, da der Programmierer dann in der Lage ist, zu melden, wo der Fehler in stderr, einer Protokolldatei oder einem entfernten Server aufgetreten ist, bevor er das Programm zum Absturz bringt. Ich stelle mir das so vor: Der Programmierer behält globale Variablen, speichert den Namen der aktuell verarbeiteten Funktion (anderes Zeug für Multi-Thread-Programme) und die aktuelle Zeilennummer (die Zeilennummer-Variablen-Update-Anweisungen (auf Kosten der Rechenleistung) wären automatisch in jede Zeile eingefügt). Wenn ein Segfault auftritt, kann der Programmierer „SIGSEGV in tois()@main.c:492“ an das Protokoll melden.

    – sij

    13. Oktober 2020 um 5:32 Uhr

Benutzeravatar von Champignac
Champignac

Ich stimme der Aussage überhaupt nicht zu “Fang nicht den SIGSEGV”.

Das ist eine ziemlich gute Praxis, damit umzugehen unerwartet Bedingungen. Und das ist viel sauberer zu bewältigen NULL Zeiger (wie durch Malloc-Ausfälle gegeben) mit zugehörigem Signalmechanismus setjmp/longjmpals die Verwaltung von Fehlerbedingungen über Ihren gesamten Code zu verteilen.

Beachten Sie jedoch, dass, wenn Sie ”sigaction” verwenden, on SEGVdarfst du nicht vergessen zu sagen SA_NODEFER in sa_flags – oder einen anderen Weg finden, mit der Tatsache umzugehen SEGV wird Ihren Handler nur einmal auslösen.

#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>

static void do_segv()
{
  int *segv;

  segv = 0; /* malloc(a_huge_amount); */

  *segv = 1;
}

sigjmp_buf point;

static void handler(int sig, siginfo_t *dont_care, void *dont_care_either)
{
   longjmp(point, 1);
}

int main()
{
  struct sigaction sa;

  memset(&sa, 0, sizeof(sigaction));
  sigemptyset(&sa.sa_mask);

  sa.sa_flags     = SA_NODEFER;
  sa.sa_sigaction = handler;

  sigaction(SIGSEGV, &sa, NULL); /* ignore whether it works or not */ 

  if (setjmp(point) == 0)
   do_segv();

  else
    fprintf(stderr, "rather unexpected error\n");

  return 0;
}

  • Dies ist eine gute Lösung. Das „abstürzen lassen und dann Core-Dump analysieren“ gefiel mir nicht besonders.

    – dturvene

    3. November 2017 um 21:10 Uhr

  • Beachten Sie, dass Sie tun Fehlerüberprüfungen müssen immer noch mit diesem Stil verbreitet werden, aber auf andere Weise. Hier muss der aufrufende Code wissen, wie er im Fehlerfall alle Ressourcen für den gesamten aufgerufenen Code bereinigen kann. Im traditionellen Stil prüft jeder Codeabschnitt auf Fehler und bereinigt dann nur seine eigenen Ressourcen. IMO ist letzteres vorzuziehen, da jedes Stück weniger über die anderen wissen muss.

    – gntskn

    20. Juli 2020 um 16:49 Uhr

  • Super nützliche Antwort, danke. Siehe auch Abschnitt „Normsignale“. hier… Sieht so aus, als müsste ich eine Reihe von Signalen manuell angeben, wenn ich alles erfassen möchte …

    – Andreas

    10. November 2020 um 5:27 Uhr

  • Okay, wenn Sie dies tun, können Sie verwenden strsignal um eine Zeichenfolgendarstellung des Signals zu erhalten, und dann können Sie diese Zeichenfolge und die ausdrucken/protokollieren sig int selbst generisch. Dann können Sie diesen Code für eine ganze Reihe von Signalen wiederverwenden, mit nur 1 handler() Funktion!

    – Andreas

    10. November 2020 um 5:43 Uhr


Wenn die SIGSEGV feuert wieder, die offensichtliche Schlussfolgerung ist, dass der Anruf an MyfreeBuffers(); hat nicht das zugrunde liegende Problem behoben (und wenn diese Funktion wirklich nur funktioniert free() etwas zugewiesener Speicher, ich bin mir nicht sicher, warum Sie das glauben würden).

Grob, a SIGSEGV wird ausgelöst, wenn versucht wird, auf eine unzugängliche Speicheradresse zuzugreifen. Wenn Sie die Anwendung nicht beenden möchten, müssen Sie entweder diese Speicheradresse zugänglich machen oder den Ausführungspfad mit ändern longjmp().

Sie sollten nicht versuchen, danach fortzufahren SIG_SEGV. Es bedeutet im Grunde, dass die Umgebung Ihrer Anwendung in irgendeiner Weise beschädigt ist. Es könnte sein, dass Sie gerade einen Nullzeiger dereferenziert haben, oder es könnte sein, dass ein Fehler dazu geführt hat, dass Ihr Programm seinen Stack oder den Heap oder eine Zeigervariable beschädigt hat, Sie wissen es einfach nicht. Das nur sicher ist, das Programm zu beenden.

Es ist vollkommen legitim, mit Control-C umzugehen. Viele Anwendungen tun dies, aber Sie müssen wirklich genau aufpassen, was Sie in Ihrem Signal-Handler tun. Sie können keine Funktion aufrufen, die nicht wiedereintrittsfähig ist. Das heißt also, wenn Ihr MyFreeBuffers() ruft die stdlib auf free() Funktion, Sie sind wahrscheinlich geschraubt. Wenn der Benutzer Strg-C drückt, während sich das Programm in der Mitte befindet malloc() oder free() und daher werden Sie nach der Hälfte der Bearbeitung der Datenstrukturen, die sie zum Verfolgen von Heap-Zuweisungen verwenden, mit ziemlicher Sicherheit den Heap beschädigen, wenn Sie anrufen malloc() oder free() im Signalhandler.

Das einzig Sichere, was Sie in einem Signal-Handler tun können, ist das Setzen eines Flags, das besagt, dass Sie das Signal empfangen haben. Ihre App kann dann das Flag in Intervallen abfragen, um zu entscheiden, ob sie eine Aktion ausführen muss.

Nun, Sie könnten eine Zustandsvariable setzen und nur Speicher freigeben, wenn sie nicht gesetzt ist. Der Signalhandler wird jedes Mal aufgerufen, Sie können das AFAIK nicht kontrollieren.

Benutzeravatar von newlogic
neueLogik

Ich sehe einen Fall für die Wiederherstellung nach einem SIG_SEGV. Wenn Sie Ereignisse in einer Schleife behandeln und eines dieser Ereignisse eine Segmentierungsverletzung verursacht, möchten Sie nur dieses Ereignis überspringen und die Verarbeitung der verbleibenden Ereignisse fortsetzen. In meinen Augen ähnelt SIG_SEGV der NullPointerException in Java. Ja, der Status wird nach beiden inkonsistent und unbekannt sein, aber in einigen Fällen möchten Sie die Situation handhaben und weitermachen. Zum Beispiel würden Sie beim Algo-Handel die Ausführung einer Order anhalten und einem Händler erlauben, manuell zu übernehmen, ohne das gesamte System zum Absturz zu bringen und alle anderen Orders zu ruinieren.

Benutzeravatar der Community
Gemeinschaft

Sieht so aus, als könnte zumindest unter Linux der Trick mit der Option -fnon-call-exceptions die Lösung sein. Es bietet die Möglichkeit, das Signal in eine allgemeine C++-Ausnahme zu konvertieren und auf allgemeine Weise zu behandeln. Schauen Sie sich linux3/gcc46: “-fnon-call-exceptions” an, welche Signale sind Trapping-Anweisungen? zum Beispiel.

1403280cookie-checkBehandlung von Segmentierungsfehlern

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy