Ce que je sais, c'est que les variables globales et statiques sont stockées dans le .data
segment et que les données non initialisées sont dans le .bss
segment. Ce que je ne comprends pas, c'est pourquoi avons-nous un segment dédié pour les variables non initialisées? Si une variable non initialisée a une valeur affectée au moment de l'exécution, la variable existe-t-elle encore dans le .bss
segment uniquement?
Dans le programme suivant, a
est dans le .data
segment, et b
est dans le .bss
segment; Est-ce exact? Veuillez me corriger si ma compréhension est fausse.
#include <stdio.h>
#include <stdlib.h>
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
int b[20]; /* Uninitialized, so in the .bss and will not occupy space for 20 * sizeof (int) */
int main ()
{
;
}
Pensez également à suivre le programme,
#include <stdio.h>
#include <stdlib.h>
int var[10]; /* Uninitialized so in .bss */
int main ()
{
var[0] = 20 /* **Initialized, where this 'var' will be ?** */
}
c
linux
compiler-construction
Qui suis je
la source
la source
Réponses:
La raison est de réduire la taille du programme. Imaginez que votre programme C s'exécute sur un système embarqué, où le code et toutes les constantes sont enregistrés dans une vraie ROM (mémoire flash). Dans de tels systèmes, une "copie vers le bas" initiale doit être exécutée pour définir tous les objets de durée de stockage statique, avant l'appel de main (). Cela ressemblera généralement à ce pseudo:
Où .data et .bss sont stockés dans la RAM, mais init_value est stocké dans la ROM. S'il s'agissait d'un segment, la ROM devait être remplie de beaucoup de zéros, ce qui augmentait considérablement la taille de la ROM.
Les exécutables basés sur la RAM fonctionnent de la même manière, bien qu'ils n'aient bien sûr pas de véritable ROM.
En outre, memset est probablement un assembleur en ligne très efficace, ce qui signifie que la copie de démarrage peut être exécutée plus rapidement.
la source
Le
.bss
segment est une optimisation. Le.bss
segment entier est décrit par un seul nombre, probablement 4 octets ou 8 octets, qui donne sa taille dans le processus en cours, alors que la.data
section est aussi grande que la somme des tailles des variables initialisées. Ainsi, le.bss
rend les exécutables plus petits et plus rapides à charger. Sinon, les variables pourraient être dans le.data
segment avec une initialisation explicite à des zéros; le programme aurait du mal à faire la différence. (En détail, l'adresse des objets dans.bss
serait probablement différente de l'adresse si elle était dans le.data
segment.)Dans le premier programme,
a
serait dans le.data
segment etb
serait dans le.bss
segment de l'exécutable. Une fois le programme chargé, la distinction devient sans importance. Au moment de l'exécution,b
occupe des20 * sizeof(int)
octets.Dans le deuxième programme, l'
var
espace est alloué et l'affectationmain()
modifie cet espace. Il se trouve que l'espace pour avar
été décrit dans le.bss
segment plutôt que dans le.data
segment, mais cela n'affecte pas la façon dont le programme se comporte lors de l'exécution.la source
edata
). Concrètement, le .bss n'existe pas en mémoire une fois la mémoire image terminée; les données mises à zéro sont une simple partie de la section .data. Mais les détails varient selon les o / s etc.Depuis le langage d'assemblage pas à pas: Programmation avec Linux par Jeff Duntemann, concernant la section .data :
et la section .bss :
la source
Eh bien, tout d'abord, ces variables de votre exemple ne sont pas non initialisées; C spécifie que les variables statiques non initialisées par ailleurs sont initialisées à 0.
Donc, la raison de .bss est d'avoir des exécutables plus petits, économisant de l'espace et permettant un chargement plus rapide du programme, car le chargeur peut simplement allouer un tas de zéros au lieu d'avoir à copier les données du disque.
Lors de l'exécution du programme, le chargeur de programme chargera .data et .bss en mémoire. Les écritures dans des objets résidant dans .data ou .bss ne vont donc qu'en mémoire, elles ne sont à aucun moment vidées vers le binaire sur le disque.
la source
Le System V ABI 4.1 (1997) (spécification AKA ELF) contient également la réponse:
dit que le nom de la section
.bss
est réservé et a des effets spéciaux, en particulier il n'occupe aucun espace fichier , donc l'avantage par dessus.data
.L'inconvénient est bien sûr que tous les octets doivent être définis sur
0
lorsque le système d'exploitation les met en mémoire, ce qui est plus restrictif, mais un cas d'utilisation courant, et fonctionne bien pour les variables non initialisées.La
SHT_NOBITS
documentation du type de section répète cette affirmation:Le standard C ne dit rien sur les sections, mais nous pouvons facilement vérifier où la variable est stockée sous Linux avec
objdump
etreadelf
, et conclure que les globaux non initialisés sont en fait stockés dans le.bss
. Voir par exemple cette réponse: Qu'arrive - t-il à une variable déclarée non initialisée en C?la source
L'article de wikipedia .bss fournit une belle explication historique, étant donné que le terme est du milieu des années 1950 (yippee mon anniversaire ;-).
À l'époque, chaque bit était précieux, donc toute méthode de signalisation d'espace vide réservé était utile. Ce ( .bss ) est celui qui est bloqué.
Les sections .data sont réservées aux espaces qui ne sont pas vides, mais contiennent (vos) valeurs définies.
la source