In questa pagina:
- Panoramica
- Esempi di comuni segfault
- Trovare out-of-bounds array di riferimenti
- Controllare la shell limiti
- Utilizzare un debugger per diagnosticare segfault
Descrizione
Un errore di segmentazione (aka segmentation fault) è una condizione comune che causa l’arresto anomalo dei programmi; essi sono spesso associati con un file chiamato core
. I segfault sono causati da un programma che tenta di leggere o scrivere una posizione di memoria non valida.
la memoria del Programma è diviso in diversi segmenti: un segmento di testo per le istruzioni di un programma, di un segmento di dati per le variabili e gli array definiti in fase di compilazione, un segmento di stack per temporanea (o automatico) le variabili definite in subroutine e funzioni, e un segmento di heap per le variabili allocate durante il runtime funzioni, come ad esempio malloc
(in C) e allocate
(in Fortran). Per ulteriori informazioni, vedere Informazioni sui segmenti del programma.
Un segfault si verifica quando un riferimento a una variabile non rientra nel segmento in cui risiede tale variabile o quando viene tentata una scrittura in una posizione che si trova in un segmento di sola lettura. In pratica, i segfault sono quasi sempre dovuti al tentativo di leggere o scrivere un elemento array inesistente, non definendo correttamente un puntatore prima di usarlo, o (nei programmi C) usando accidentalmente il valore di una variabile come indirizzo (vedere l’esempio scanf
sotto).
Esempi di comuni segfault
- Per esempio, chiamando
memset()
come mostrato di seguito causerebbe un programma di segmentation fault:memset((char *)0x0, 1, 100);
- I seguenti tre casi di illustrare i più comuni tipi di array-correlati segmentation fault:
Caso /* "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;
Caso 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;
C /* Illegal memory access because no memory is allocated for foo2 */ float *foo, *foo2; foo = (float*)malloc(1000); foo2 = 1.0;
- Nel caso di Un array
foo
è definito perindex = 0,1, 2, ... 999
. Tuttavia, nell’ultima iterazione del ciclofor
, il programma tenta di accedere afoo
. Ciò comporterà un segfault se tale posizione di memoria si trova al di fuori del segmento di memoria in cui risiedefoo
. Anche se non causa un segfault, è ancora un bug. - Nel caso B, integer
n
potrebbe essere qualsiasi valore casuale. Come nel caso A, se non è nell’intervallo0, 1, ... 999
, potrebbe causare un segfault. Che lo faccia o no, è certamente un bug. - Nel caso C, l’allocazione della memoria per la variabile
foo2
è stata trascurata, quindifoo2
punterà a una posizione casuale in memoria. L’accesso afoo2
probabilmente comporterà un segfault.
- Nel caso di Un array
- Un altro errore di programmazione comune che porta a segfaults è la supervisione nell’uso dei puntatori. Ad esempio ,la funzione C
scanf()
prevede l’indirizzo di una variabile come secondo parametro; pertanto, quanto segue probabilmente causerà il crash del programma con un segfault:int foo = 0; scanf("%d", foo); /* Note missing & sign ; correct usage would have been &foo */
La variabile
foo
potrebbe essere definito a posizione di memoria1000
, ma al di sopra di chiamata di funzione vorresti provare a leggere i dati integer in posizione di memoria0
secondo la definizione difoo
. - Un segfault si verifica quando un programma tenta di operare su una posizione di memoria in un modo non consentito (ad esempio, i tentativi di scrivere una posizione di sola lettura comporterebbero un segfault).
- I segfault possono verificarsi anche quando il programma esaurisce lo spazio dello stack. Questo potrebbe non essere un bug nel tuo programma, ma potrebbe essere dovuto invece alla tua shell che imposta il limite di dimensione dello stack troppo piccolo.
Trova riferimenti di array out-of-bounds
La maggior parte dei compilatori Fortran ha un’opzione che inserirà il codice per eseguire il controllo dei limiti su tutti i riferimenti di array durante il runtime. Se un accesso non rientra nell’intervallo di indice definito per un array, il programma si fermerà e ti dirà dove si verifica questo. Per la maggior parte dei compilatori Fortran, l’opzione è -C
, o -check
seguito da una parola chiave. Vedere la guida dell’utente del compilatore per ottenere l’opzione esatta. Usa il controllo dei limiti solo durante il debug, poiché rallenterà il tuo programma. Alcuni compilatori C hanno anche un’opzione di controllo dei limiti.
Controlla i limiti della shell
Come notato nell’ultimo esempio sopra, alcuni problemi di segfault non sono dovuti a bug nel tuo programma, ma sono causati invece da limiti di memoria di sistema troppo bassi. Di solito è il limite alla dimensione dello stack che causa questo tipo di problema. Per verificare i limiti di memoria, utilizzare il tag ulimit
comando bash
o ksh
o limit
comando csh
o tcsh
. Prova a impostare stacksize più in alto, quindi esegui nuovamente il programma per vedere se segfault scompare.
Usa i debugger per diagnosticare segfaults
Se non riesci a trovare il problema in altro modo, potresti provare un debugger. Ad esempio, è possibile utilizzare il noto debugger di GNU GDB
per visualizzare il backtrace di un file core
scaricato dal programma; ogni volta che i programmi segfault, di solito scaricano il contenuto della (loro sezione della) memoria al momento dell’arresto in un file core
. Avviare il debugger con il comandogdb core
, quindi utilizzare il comandobacktrace
per vedere dove si trovava il programma quando si è schiantato. Questo semplice trucco ti permetterà di concentrarti su quella parte del codice.
Se si utilizzabacktrace
sul filecore
g non si trova il problema, potrebbe essere necessario eseguire il programma sotto controllo debugger, quindi passare attraverso la funzione code one, o una riga di codice sorgente, alla volta. Per fare ciò, dovrai compilare il tuo codice senza ottimizzazione e con il flag -g
, quindi le informazioni sulle righe del codice sorgente saranno incorporate nel file eseguibile. Per ulteriori informazioni, vedere l’esempio passo-passo per l’utilizzo di GDB all’interno di Emacs per eseguire il debug di un programma C orC++.