Was ist ein Segmentierungsfehler?

598

Was ist ein Segmentierungsfehler? Ist es anders in C und C ++? Wie hängen Segmentierungsfehler und baumelnde Zeiger zusammen?

Rajendra Uppal
quelle
93
Durch einen Segmentierungsfehler fühlt sich der Compiler schlecht .
Benjamin Crouzier
22
Wenn dies der Fall ist, warum hat sich der Compiler in meinem Fall nicht beschwert, alles lief reibungslos, aber zur Laufzeit löst das System einen Segmentierungsfehler (Core Dump) aus? T_T
Jim Raynor
3
Nur ein Speicherauszug, wenn etwas schief geht!
Ergebnisseway
7
@pinouchon: Witzig, aber wann hat ein Compiler etwas mit Seg-Fehlern zu tun? Ist es nicht mehr die Laufzeitumgebung?
Dhein
1
Wird normalerweise aufgerufen, indem versucht wird, einen Nullzeiger zu dereferenzieren. Daher ist ein Segmentierungsfehler häufig analog zu einem Java NullPointerException.
Raedwald

Antworten:

673

Ein Segmentierungsfehler ist eine bestimmte Art von Fehler, der durch den Zugriff auf Speicher verursacht wird, der „Ihnen nicht gehört“. Es ist ein Hilfsmechanismus, der Sie davon abhält, den Speicher zu beschädigen und schwer zu debuggende Speicherfehler einzuführen. Immer wenn Sie einen Segfault erhalten, wissen Sie, dass Sie mit dem Speicher etwas falsch machen - auf eine bereits freigegebene Variable zugreifen, in einen schreibgeschützten Teil des Speichers schreiben usw. Der Segmentierungsfehler ist in den meisten Sprachen, mit denen Sie sich herumschlagen können, im Wesentlichen der gleiche Bei der Speicherverwaltung gibt es keinen grundsätzlichen Unterschied zwischen Segfaults in C und C ++.

Es gibt viele Möglichkeiten, einen Segfault zu erhalten, zumindest in den untergeordneten Sprachen wie C (++). Ein üblicher Weg, um einen Segfault zu erhalten, besteht darin, einen Nullzeiger zu dereferenzieren:

int *p = NULL;
*p = 1;

Ein weiterer Segfault tritt auf, wenn Sie versuchen, in einen Teil des Speichers zu schreiben, der als schreibgeschützt markiert wurde:

char *str = "Foo"; // Compiler marks the constant string as read-only
*str = 'b'; // Which means this is illegal and results in a segfault

Der baumelnde Zeiger zeigt auf etwas, das es nicht mehr gibt, wie hier:

char *p = NULL;
{
    char c;
    p = &c;
}
// Now p is dangling

Der Zeiger pbaumelt, weil er auf eine Zeichenvariable zeigt c, die nach dem Ende des Blocks nicht mehr existiert. Und wenn Sie versuchen, einen baumelnden Zeiger (wie *p='A') zu dereferenzieren , erhalten Sie wahrscheinlich einen Segfault.

Zoul
quelle
154
Das letzte Beispiel ist besonders böse, wenn ich baue: int main () {char * p = 0; {char c = 'x'; p = & c; } printf ("% c \ n", * p); return 0; } Mit gcc oder mehreren anderen Compilern scheint es zu funktionieren. Keine Warnungen beim Kompilieren. Kein Segfault. Dies liegt daran, dass das '}' außerhalb des Gültigkeitsbereichs die Daten nicht löscht, sondern nur als frei markiert, um erneut verwendet zu werden. Der Code kann jahrelang auf einem Produktionssystem einwandfrei funktionieren. Sie ändern einen anderen Teil des Codes, ändern den Compiler oder etwas anderes und BOOOOOM!
Chris Huang-Leaver
36
Entschuldigung für die Beule, aber nur eine Randnotiz ... keines Ihrer Beispiele verursacht notwendigerweise einen Segfault, tatsächlich ist es nur undefiniertes Verhalten ;-)
oldrinb
18
@oldrinb: Es ist unmöglich, Code zu schreiben, der notwendigerweise einen Segfault verursacht. Nicht zuletzt, weil es Systeme gibt, die ohne Speicherschutz arbeiten und daher nicht sagen können, ob ein Speicher tatsächlich "Ihnen gehört", und daher keine Fehler kennen, sondern nur undefiniertes Verhalten ... (zum Beispiel klassisches AmigaOS)
DevSolar
7
@ ChrisHuang-Leaver, Sie müssen verstehen, dass dies clokal ist. Dies bedeutet, dass es nachher auf den Stapel geschoben {und danach herausgepoppt wurde }. Der baumelnde Zeiger ist nur eine Referenz auf einen Versatz, der sich jetzt außerhalb des Stapels befindet. Aus diesem Grund wird das Ändern in einem einfachen Programm niemals einen Segfault auslösen. Andererseits kann es in einem komplexeren Anwendungsfall zu einem Segfault kommen, bei dem andere Funktionsaufrufe dazu führen können, dass der Stapel wächst und die Daten enthält, auf die der baumelnde Zeiger zeigt. Das Schreiben auf diese Daten (lokale Variablen) würde zu undefiniertem Verhalten führen (Segfault & Co)
Ayman Khamouma
3
@ ChrisHuang-Leaver, normalerweise muss der Compiler, wenn Sie den Gültigkeitsbereich verlassen, etwas Stapelspeicherplatz wiederherstellen, um den nicht verwendeten Stapelspeicherplatz freizugeben. Dies geschieht jedoch nicht immer (wobei gcc einer dieser Compiler ist). Außerdem wird der zugewiesene Stapelspeicher normalerweise wieder verwendet, sodass ich von keinem Betriebssystem gehört habe, das nicht verwendete Stapelseiten an das System zurückgibt, sodass dieser Speicherplatz für a reserviert ist. Daher SIGSEGVerwarte ich nicht, dass ein solches Signal den Stapel beschädigt.
Luis Colorado
111

Es ist erwähnenswert, dass ein Segmentierungsfehler nicht durch den direkten Zugriff auf einen anderen Prozessspeicher verursacht wird (das höre ich manchmal), da dies einfach nicht möglich ist. Mit virtuellem Speicher hat jeder Prozess seinen eigenen virtuellen Adressraum und es gibt keine Möglichkeit, mit einem Zeigerwert auf einen anderen zuzugreifen. Eine Ausnahme bilden gemeinsam genutzte Bibliotheken, bei denen es sich um denselben physischen Adressraum handelt, der (möglicherweise) verschiedenen virtuellen Adressen zugeordnet ist, und um Kernelspeicher, die in jedem Prozess sogar auf dieselbe Weise zugeordnet werden (um zu vermeiden, dass TLB bei syscall geleert wird, denke ich). Und Dinge wie shmat;) - das zähle ich als "indirekten" Zugang. Man kann jedoch überprüfen, ob sie sich normalerweise weit vom Prozesscode entfernt befinden und wir normalerweise darauf zugreifen können (deshalb sind sie dort,

Dennoch kann ein Segmentierungsfehler auftreten, wenn auf nicht ordnungsgemäße Weise auf unseren eigenen (Prozess-) Speicher zugegriffen wird (z. B. wenn versucht wird, in einen nicht beschreibbaren Speicherplatz zu schreiben). Der häufigste Grund dafür ist jedoch der Zugriff auf den Teil des virtuellen Adressraums, der überhaupt keinem physischen zugeordnet ist.

Und das alles in Bezug auf virtuelle Speichersysteme.

konrad.kruczynski
quelle
Mit gemeinsam genutzten Speicher- / Speicherzuordnungsdateien kann jemand anderes mit Ihrem Speicher herumspielen. In WIN32 gibt es auch böse APIs wie 'WriteProcessMemory'!
Paulm
1
@ Paulm: Ja, ich weiß. Dies ist, was ich in "Und Dinge wie shmat;) - das ist, was ich als" indirekten "Zugang zähle."
konrad.kruczynski
In einem Betriebssystem mit virtuellem Speicher gibt es keine Möglichkeit (normalerweise, also bitte, Betriebssystemimplementierer, flammen mich nicht dafür), dass ein Prozess auf einen anderen virtuellen Prozessspeicher zugreift, da es sich nicht um eine Art Systemanruf zum Anhängen von Speicher handelt, der dies ermöglicht Zugriff. Virtuelle Speicheradressen bedeuten normalerweise je nach dem betrachteten Prozess unterschiedliche Bedeutungen.
Luis Colorado
38

Ein Segmentierungsfehler wird durch eine Anforderung für eine Seite verursacht, die der Prozess nicht in seiner Deskriptortabelle aufgeführt hat, oder durch eine ungültige Anforderung für eine Seite, die er aufgelistet hat (z. B. eine Schreibanforderung auf einer schreibgeschützten Seite).

Ein baumelnder Zeiger ist ein Zeiger, der auf eine gültige Seite zeigen kann oder nicht, aber auf ein "unerwartetes" Speichersegment zeigt.

Ignacio Vazquez-Abrams
quelle
10
Dies ist wahr, aber würde es Ihnen wirklich helfen, wenn Sie bereits nicht wüssten, was ein Segmentierungsfehler ist?
Zoul
29

Um ehrlich zu sein, hat Wikipedia, wie andere Poster bereits erwähnt haben, einen sehr guten Artikel dazu. Schauen Sie sich das an. Diese Art von Fehler ist sehr häufig und wird häufig als andere Dinge bezeichnet, z. B. Zugriffsverletzung oder allgemeiner Schutzfehler.

Sie unterscheiden sich nicht in C, C ++ oder einer anderen Sprache, die Zeiger zulässt. Diese Art von Fehlern wird normalerweise durch Zeiger verursacht

  1. Wird vor der ordnungsgemäßen Initialisierung verwendet
  2. Wird verwendet, nachdem der Speicher, auf den sie verweisen, neu zugewiesen oder gelöscht wurde.
  3. Wird in einem indizierten Array verwendet, in dem der Index außerhalb der Arraygrenzen liegt. Dies ist im Allgemeinen nur dann der Fall, wenn Sie Zeigerberechnung für herkömmliche Arrays oder C-Strings durchführen, nicht für STL / Boost-basierte Sammlungen (in C ++).
Komponente 10
quelle
16

Laut Wikipedia:

Ein Segmentierungsfehler tritt auf, wenn ein Programm versucht, auf einen Speicherort zuzugreifen, auf den es nicht zugreifen darf, oder wenn es versucht, auf einen Speicherort zuzugreifen, der nicht zulässig ist (z. B. beim Versuch, an einen schreibgeschützten Speicherort zu schreiben, oder einen Teil des Betriebssystems überschreiben).

Orhan Cinar
quelle
13

Ein Segmentierungsfehler wird auch durch Hardwarefehler verursacht, in diesem Fall die RAM-Speicher. Dies ist die seltenere Ursache. Wenn Sie jedoch keinen Fehler in Ihrem Code finden, kann Ihnen möglicherweise ein Memtest helfen.

Die Lösung in diesem Fall ändern Sie den RAM.

bearbeiten:

Hier gibt es eine Referenz: Segmentierungsfehler nach Hardware

Alejo Bernardin
quelle
3
Ein schneller und schmutziger Test für fehlerhaften RAM besteht darin, Ihr abstürzendes Programm immer wieder in einer Schleife auszuführen. Wenn das Programm keinen internen Nichtdeterminismus hat - das heißt, es erzeugt immer die gleiche Ausgabe für die gleiche Eingabe oder sollte es zumindest -, aber für eine bestimmte Eingabe stürzt es manchmal ab , nicht immer, aber auch nicht nie: dann sollten Sie Sorgen Sie sich um schlechten Arbeitsspeicher.
zwol
8

Ein Segmentierungsfehler tritt auf, wenn ein Prozess (eine laufende Instanz eines Programms) versucht, auf die schreibgeschützte Speicheradresse oder den Speicherbereich zuzugreifen, der von einem anderen Prozess verwendet wird, oder auf die nicht vorhandene (ungültige) Speicheradresse zuzugreifen. Dangling Reference (Zeiger) Problem bedeutet, dass versucht wird, auf ein Objekt oder eine Variable zuzugreifen, deren Inhalt bereits aus dem Speicher gelöscht wurde, z.

int *arr = new int[20];
delete arr;
cout<<arr[1];  //dangling problem occurs here
Sohail xIN3N
quelle
4
Der richtige Weg, ein Array zu löschen, ist delete [] arr;
Damian
8

Die Segmentation_fault- Seite von Wikipedia enthält eine sehr schöne Beschreibung, in der nur die Ursachen und Gründe aufgezeigt werden. Schauen Sie im Wiki nach einer detaillierten Beschreibung.

Beim Rechnen ist ein Segmentierungsfehler (häufig mit Segfault abgekürzt) oder eine Zugriffsverletzung ein Fehler, der von Hardware mit Speicherschutz ausgelöst wird und ein Betriebssystem (OS) über eine Speicherzugriffsverletzung benachrichtigt.

Im Folgenden sind einige typische Ursachen für einen Segmentierungsfehler aufgeführt:

  • Dereferenzieren von NULL-Zeigern - Dies wird speziell von der Speicherverwaltungshardware festgelegt
  • Versuch, auf eine nicht vorhandene Speicheradresse zuzugreifen (außerhalb des Adressraums des Prozesses)
  • Beim Versuch, auf den Speicher zuzugreifen, hat das Programm keine Rechte (z. B. Kernelstrukturen im Prozesskontext).
  • Versuch, Nur-Lese-Speicher zu schreiben (z. B. Codesegment)

Diese wiederum werden häufig durch Programmierfehler verursacht, die zu einem ungültigen Speicherzugriff führen:

  • Dereferenzieren oder Zuweisen zu einem nicht initialisierten Zeiger (wilder Zeiger, der auf eine zufällige Speicheradresse zeigt)

  • Dereferenzieren oder Zuweisen zu einem freigegebenen Zeiger (baumelnder Zeiger, der auf den freigegebenen / freigegebenen / gelöschten Speicher zeigt)

  • Ein Pufferüberlauf.

  • Ein Stapelüberlauf.

  • Versuch, ein Programm auszuführen, das nicht korrekt kompiliert wird. (Einige Compiler geben trotz Fehlern bei der Kompilierung eine ausführbare Datei aus.)

Roy
quelle
6

Mit einfachen Worten: Segmentierungsfehler ist, dass das Betriebssystem ein Signal an das Programm sendet, das besagt, dass es einen unzulässigen Speicherzugriff erkannt hat und das Programm vorzeitig beendet, um zu verhindern, dass der Speicher beschädigt wird.

FilipeCanatto
quelle
3

"Segmentierungsfehler" bedeutet, dass Sie versucht haben, auf Speicher zuzugreifen, auf den Sie keinen Zugriff haben.

Das erste Problem ist mit Ihren Argumenten von main. Die Hauptfunktion sollte sein int main(int argc, char *argv[]), und Sie sollten überprüfen, ob argc mindestens 2 ist, bevor Sie auf argv [1] zugreifen.

Da Sie ein Float an printf übergeben (das übrigens beim Übergeben an printf in ein Double konvertiert wird), sollten Sie den Formatbezeichner% f verwenden. Der% s-Formatbezeichner gilt für Zeichenfolgen ('\ 0'-terminierte Zeichenarrays).

PHP Wurm ...
quelle
2

Ein Segmentierungsfehler oder eine Zugriffsverletzung tritt auf, wenn ein Programm versucht, auf einen nicht vorhandenen Speicherort zuzugreifen, oder wenn versucht wird, auf eine nicht zulässige Weise auf einen Speicherort zuzugreifen.

 /* "Array out of bounds" error 
   valid indices for array foo
   are 0, 1, ... 999 */
   int foo[1000];
   for (int i = 0; i <= 1000 ; i++) 
   foo[i] = i;

Hier existiert i [1000] nicht, so dass ein Segfault auftritt.

Ursachen des Segmentierungsfehlers:

it arise primarily due to errors in use of pointers for virtual memory addressing, particularly illegal access.

De-referencing NULL pointers  this is special-cased by memory management hardware.

Attempting to access a nonexistent memory address (outside processs address space).

Attempting to access memory the program does not have rights to (such as kernel structures in process context).

Attempting to write read-only memory (such as code segment).
Mohit Rohilla
quelle
2
Erstens hat der Seg-Fehler nichts mit der Adresse zu tun, die existiert oder nicht existiert. Es geht darum, dass Sie darauf zugreifen, wo Sie dies nicht dürfen. Und in Ihrem speziellen Beispiel wird sogar standardmäßig garantiert, dass dieser Ort existiert. da der Standard im Array-Fall sagt, muss angegeben werden, dass es eine gültige Adresse für einen Zeigerpunktg auf einem gut ausgerichteten Array innerhalb seiner Grenzen UND 1 dahinter gibt .
Dhein
Es wird auch mit Adresse angezeigt, wenn Sie die Adresse nicht haben und wenn Sie versuchen, auf diese Adresse zuzugreifen, gibt es auch seg. Fehler. Und in meinem Beispiel ist es nur zum Verständnis der Sichtweise.
Mohit Rohilla
2

Es gibt mehrere gute Erklärungen für "Segmentierungsfehler" in den Antworten, aber da bei Segmentierungsfehlern häufig ein Speicherauszug des Speicherinhalts vorhanden ist, wollte ich mitteilen, wo die Beziehung zwischen dem Teil "Kernspeicherauszug" im Segmentierungsfehler (Kernspeicherauszug) und Erinnerung kommt von:

Von etwa 1955 bis 1975 - vor dem Halbleiterspeicher - verwendete die vorherrschende Technologie im Computerspeicher winzige magnetische Donuts, die auf Kupferdrähten aufgereiht waren. Die Donuts waren als "Ferritkerne" und der Hauptspeicher als "Kernspeicher" oder "Kern" bekannt.

Von hier genommen .

Viktor Nonov
quelle
2

Es gibt genügend Definitionen für Segmentierungsfehler. Ich möchte einige Beispiele anführen, die mir beim Programmieren begegnet sind. Diese Fehler mögen albern erscheinen, verschwenden aber viel Zeit.

  1. Im folgenden Fall kann ein Segmentierungsfehler auftreten, wenn der Argumet-Typ in printf nicht übereinstimmt

    #include<stdio.h> int main(){
    int a = 5; printf("%s",a); return 0; }

Ausgabe : Segmentation Fault (SIGSEGV)

  1. wenn Sie vergessen haben, einem Zeiger Speicher zuzuweisen, aber versucht haben, ihn zu verwenden.

     #include<stdio.h> 
     typedef struct{
       int a;
     }myStruct;   
    int main(){
      myStruct *s;
      /* few lines of code */
      s->a = 5;
      return 0;
    }

Ausgabe : Segmentation Fault (SIGSEGV)

NPE
quelle
1

Eine einfache Bedeutung von Segmentation faultist, dass Sie versuchen, auf einen Speicher zuzugreifen, der Ihnen nicht gehört. Segmentation faulttritt auf, wenn wir versuchen, Aufgaben an einem Nur-Lese-Speicherort zu lesen und / oder zu schreiben oder Speicher freizugeben. Mit anderen Worten, wir können dies als eine Art Speicherbeschädigung erklären.

Im Folgenden erwähne ich häufige Fehler von Programmierern, die dazu führen Segmentation fault.

  • Verwendung scanf()in falschem Weg (vergessen zu setzen &).
int num;
scanf("%d", num);// must use &num instead of num
  • Verwenden Sie Zeiger falsch.
int *num; 
printf("%d",*num); //*num should be correct as num only
//Unless You can use *num but you have to point this pointer to valid memory address before accessing it.
  • Ändern eines Zeichenfolgenliteral (Zeiger versuchen, einen Nur-Lese-Speicher zu schreiben oder zu ändern.)
char *str;  

//Stored in read only part of data segment
str = "GfG";      

//Problem:  trying to modify read only memory
*(str+1) = 'n';
  • Versuchen Sie, über eine bereits freigegebene Adresse zu gelangen.
// allocating memory to num 
int* num = malloc(8); 
*num = 100; 

// de-allocated the space allocated to num 
free(num); 

// num is already freed there for it cause segmentation fault
*num = 110; 
  • Stapelüberlauf -: Der Speicher auf dem Stapel geht zur Neige
  • Zugriff auf ein Array außerhalb der Grenzen '
  • Verwenden Sie falsche Formatbezeichner, wenn Sie printf()und scanf()'
Kalana
quelle