na tej stronie:
- przegląd
- przykłady typowych segfaults
- Znajdź odniesienia do tablic zewnętrznych
- Sprawdź limity powłoki
- użyj debuggery do diagnozowania segfaults
przegląd
błąd segmentacji (znany również jako segfault) jest częstym stanem powodującym awarię programów; często są one powiązane z plikiem o nazwie core
. Segfaults są spowodowane przez program próbujący odczytać lub zapisać nielegalną lokalizację pamięci.
pamięć programu jest podzielona na różne segmenty: segment tekstowy dla instrukcji programu, segment danych dla zmiennych i tablic zdefiniowanych w czasie kompilacji, segment stosu dla zmiennych tymczasowych (lub automatycznych) zdefiniowanych w podprogramach i funkcjach oraz segment sterty dla zmiennych przydzielonych podczas wykonywania przez funkcje, takie jakmalloc
(w C) iallocate
(w Fortran). Aby uzyskać więcej informacji, zobacz informacje o segmentach programu.
funkcja segfault występuje, gdy odwołanie do zmiennej wypada poza segment, w którym znajduje się ta zmienna, lub gdy próbuje się zapisać do lokalizacji, która znajduje się w segmencie tylko do odczytu. W praktyce, segfaults są prawie zawsze spowodowane próbą odczytania lub zapisania nieistniejącego elementu tablicy, nieprawidłowym zdefiniowaniem wskaźnika przed jego użyciem lub (w programach C) przypadkowym użyciem wartości zmiennej jako adresu (patrz przykład scanf
poniżej).
przykłady typowych segfaults
- na przykład wywołanie
memset()
, jak pokazano poniżej, spowodowałoby segfault programu:memset((char *)0x0, 1, 100);
- następujące trzy przypadki ilustrują najczęstsze typy segfaults związanych z tablicą:
Przypadek A /* "Array out of bounds" error valid indices for array foo are 0, 1, ... 999 */ int foo; for (int i = 0; i <= 1000 ; i++) foo = i;
sprawa b /* Illegal memory access if value of n is not in the range 0, 1, ... 999 */ int n; int foo; for (int i = 0; i < n ; i++) foo = i;
sprawa C /* Illegal memory access because no memory is allocated for foo2 */ float *foo, *foo2; foo = (float*)malloc(1000); foo2 = 1.0;
- w przypadku a, tablica
foo
jest zdefiniowany dlaindex = 0,1, 2, ... 999
. Jednak w ostatniej iteracji pętlifor
program próbuje uzyskać dostęp dofoo
. Spowoduje to uszkodzenie segfault, jeśli ta lokalizacja pamięci znajduje się poza segmentem pamięci, w którym znajduje sięfoo
. Nawet jeśli nie powoduje segfault, nadal jest to błąd. - w przypadku B Liczba całkowita
n
może być dowolną wartością losową. Podobnie jak w przypadku A, jeśli nie znajduje się w zakresie0, 1, ... 999
, może to spowodować segfault. Czy tak, czy nie, jest to z pewnością błąd. - w przypadku C alokacja pamięci dla zmiennej
foo2
została pominięta, więcfoo2
będzie wskazywać na losową lokalizację w pamięci. Dostęp dofoo2
prawdopodobnie spowoduje segfault.
- w przypadku a, tablica
- Innym częstym błędem programowania, który prowadzi do segfaults jest nadzór w użyciu wskaźników. Na przykład, funkcja C
scanf()
oczekuje adresu zmiennej jako drugiego parametru; dlatego poniższe czynniki prawdopodobnie spowodują awarię programu z funkcją segfault:int foo = 0; scanf("%d", foo); /* Note missing & sign ; correct usage would have been &foo */
zmienna
foo
może być zdefiniowana w lokalizacji pamięci1000
, ale powyższe wywołanie funkcji spróbuje odczytać dane całkowite do lokalizacji pamięci0
zgodnie z definicjąfoo
. - funkcja segfault występuje, gdy program próbuje operować na lokalizacji pamięci w sposób, który nie jest dozwolony (na przykład próba napisania lokalizacji tylko do odczytu skutkowałaby funkcją segfault).
- Segfaults może również wystąpić, gdy program kończy się na stosie. Może to nie być błąd w twoim programie, ale może być spowodowane ustawieniem przez powłokę limitu rozmiaru stosu zbyt małego.
Znajdź nieobowiązkowe odwołania do tablic
Większość kompilatorów Fortran ma opcję, która wstawia kod do sprawdzania granic wszystkich odwołań do tablic w czasie wykonywania. Jeśli dostęp wykracza poza zakres indeksu zdefiniowany dla tablicy, program zatrzyma się i powie, gdzie to nastąpi. W przypadku większości kompilatorów Fortran opcja to -C
lub -check
, po którym następuje słowo kluczowe. Zapoznaj się z instrukcją obsługi kompilatora, aby uzyskać dokładną opcję. Używaj sprawdzania granic tylko podczas debugowania, ponieważ spowolni to twój program. Niektóre kompilatory C mają również opcję sprawdzania granic.
Sprawdź limity powłoki
jak wspomniano w ostatnim przykładzie powyżej, niektóre problemy z funkcją segfault nie są spowodowane błędami w programie, ale są spowodowane zbyt niskim limitem pamięci systemowej. Zazwyczaj to ograniczenie rozmiaru stosu powoduje tego rodzaju problem. Aby sprawdzić limity pamięci, użyj polecenia ulimit
w poleceniu bash
lub ksh
lub polecenia limit
w poleceniu csh
div>lub tcsh
. Spróbuj ustawić rozmiar stosu wyższy, a następnie ponownie uruchom program, aby sprawdzić, czy funkcja segfault zniknie.
użyj debuggerów do diagnozowania segfaults
Jeśli nie możesz znaleźć problemu w inny sposób, możesz spróbować debuggera. Na przykład, możesz użyć dobrze znanego debugera GNU GDB
, aby zobaczyć ślad pliku core
wyrzucanego przez twój program; ilekroć programy segfault, Zwykle zrzucają zawartość (swoją część) pamięci w momencie awarii do pliku core
. Uruchom debugger za pomocą polecenia gdb core
, a następnie użyj polecenia backtrace
, aby zobaczyć, gdzie znajdował się program, gdy uległ awarii. Ta prosta sztuczka pozwoli Ci skupić się na tej części kodu.
Jeśli użycie backtrace
w pliku core
G nie znajdzie problemu, może być konieczne uruchomienie programu pod kontrolą debuggera, a następnie przejście przez kod jednej funkcji lub jednej linii kodu źródłowego naraz. Aby to zrobić, musisz skompilować swój kod bez optymalizacji i z flagą-g
, więc informacje o liniach kodu źródłowego zostaną osadzone w pliku wykonywalnym. Aby dowiedzieć się więcej, zobacz przykład użycia GDB w Emacsie do debugowania programu C Orc++.