Sur cette page :
- Aperçu
- Exemples de segfaults courants
- Découvrez des références de tableaux hors limites
- Vérifiez les limites du shell
- Utilisez des débogueurs pour diagnostiquer les paramètres de segmentation
Aperçu
Un défaut de segmentation (aka segfault) est une condition courante qui provoque le plantage des programmes ; ils sont souvent associés à un fichier nommé core
. Les valeurs de segmentation sont causées par un programme essayant de lire ou d’écrire un emplacement de mémoire illégal.
La mémoire du programme est divisée en différents segments : un segment de texte pour les instructions du programme, un segment de données pour les variables et les tableaux définis au moment de la compilation, un segment de pile pour les variables temporaires (ou automatiques) définies dans les sous-programmes et les fonctions, et un segment de tas pour les variables allouées pendant l’exécution par des fonctions, telles que malloc
(en C) et allocate
(en Fortran). Pour en savoir plus, consultez À propos des segments de programme.
Une erreur de segmentation se produit lorsqu’une référence à une variable se situe en dehors du segment où réside cette variable, ou lorsqu’une écriture est tentée vers un emplacement situé dans un segment en lecture seule. En pratique, les valeurs de segmentation sont presque toujours dues à la tentative de lecture ou d’écriture d’un élément de tableau inexistant, à la définition incorrecte d’un pointeur avant de l’utiliser, ou (dans les programmes C) à l’utilisation accidentelle de la valeur d’une variable comme adresse (voir l’exemple scanf
ci-dessous).
Exemples de valeurs de segmentation courantes
- Par exemple, appeler
memset()
comme indiqué ci-dessous provoquerait la segmentation d’un programme :memset((char *)0x0, 1, 100);
- Les trois cas suivants illustrent les types les plus courants de valeurs de segmentation liées à un tableau :
Cas A ans le cas A, tableau
/* "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;
Cas 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;
Cas C /* Illegal memory access because no memory is allocated for foo2 */ float *foo, *foo2; foo = (float*)malloc(1000); foo2 = 1.0;
- Dans le cas A, tableau
foo
est défini pourindex = 0,1, 2, ... 999
. Cependant, dans la dernière itération de la bouclefor
, le programme essaie d’accéder àfoo
. Cela entraînera une erreur de segmentation si cet emplacement de mémoire se trouve en dehors du segment de mémoire où résidefoo
. Même si cela ne provoque pas de segfault, c’est toujours un bug. - Dans le cas B, l’entier
n
peut être n’importe quelle valeur aléatoire. Comme dans le cas A, s’il n’est pas dans la plage0, 1, ... 999
, cela peut provoquer une erreur de segmentation. Que ce soit le cas ou non, c’est certainement un bug. - Dans le cas C, l’allocation de mémoire pour la variable
foo2
a été négligée, doncfoo2
pointera vers un emplacement aléatoire en mémoire. L’accès àfoo2
entraînera probablement une erreur de segmentation.
- Dans le cas A, tableau
- Une autre erreur de programmation courante qui conduit à des valeurs de segmentation est la surveillance de l’utilisation des pointeurs. Par exemple, la fonction C
scanf()
attend l’adresse d’une variable comme deuxième paramètre ; par conséquent, ce qui suit entraînera probablement le crash du programme avec une erreur de segmentation:int foo = 0; scanf("%d", foo); /* Note missing & sign ; correct usage would have been &foo */
La variable
foo
peut être définie à l’emplacement de la mémoire1000
, mais l’appel de fonction ci-dessus essaierait de lire des données entières dans l’emplacement de la mémoire0
selon la définition defoo
. - Une erreur de segmentation se produit lorsqu’un programme tente d’opérer sur un emplacement mémoire d’une manière qui n’est pas autorisée (par exemple, une tentative d’écriture d’un emplacement en lecture seule entraînerait une erreur de segmentation).
- Des paramètres de segmentation peuvent également se produire lorsque votre programme manque d’espace de pile. Cela peut ne pas être un bogue dans votre programme, mais peut être dû à la place à votre shell définissant la limite de taille de pile trop petite.
Trouver des références de tableaux hors limites
La plupart des compilateurs Fortran ont une option qui insérera du code pour vérifier les limites de toutes les références de tableaux pendant l’exécution. Si un accès se situe en dehors de la plage d’index définie pour un tableau, le programme s’arrêtera et vous indiquera où cela se produit. Pour la plupart des compilateurs Fortran, l’option est -C
, ou -check
suivi d’un mot-clé. Consultez le guide de l’utilisateur de votre compilateur pour obtenir l’option exacte. Utilisez la vérification des limites uniquement lors du débogage, car cela ralentira votre programme. Certains compilateurs C ont également une option de vérification des limites.
Vérifiez les limites du shell
Comme indiqué dans le dernier exemple ci-dessus, certains problèmes de segfault ne sont pas dus à des bogues dans votre programme, mais sont causés par des limites de mémoire système trop basses. Habituellement, c’est la limite de taille de la pile qui cause ce genre de problème. Pour vérifier les limites de mémoire, utilisez la commande ulimit
dans bash
ou ksh
, ou la commande limit
dans csh
div> ou tcsh
. Essayez de définir la taille de la pile plus élevée, puis relancez votre programme pour voir si le défaut de segmentation disparaît.
Utilisez des débogueurs pour diagnostiquer les paramètres de segmentation
Si vous ne trouvez pas le problème autrement, vous pouvez essayer un débogueur. Par exemple, vous pouvez utiliser le débogueur bien connu de GNU GDB
pour afficher la trace arrière d’un fichier core
vidé par votre programme ; chaque fois que des programmes se segmentent, ils vident généralement le contenu de (leur section de la) mémoire au moment du plantage dans un fichier core
. Démarrez votre débogueur avec la commande gdb core
, puis utilisez la commande backtrace
pour voir où se trouvait le programme lorsqu’il s’est écrasé. Cette astuce simple vous permettra de vous concentrer sur cette partie du code.
Si l’utilisation du fichier backtrace
sur le fichier core
g ne trouve pas le problème, vous devrez peut-être exécuter le programme sous le contrôle du débogueur, puis parcourir le code une fonction, ou une ligne de code source, à la fois. Pour ce faire, vous devrez compiler votre code sans optimisation, et avec l’indicateur -g
, de sorte que les informations sur les lignes de code source seront intégrées dans le fichier exécutable. Pour en savoir plus, consultez l’exemple Étape par étape d’utilisation de GDB dans Emacs pour déboguer un programme C orC++.