Ce que je sais, c'est que les variables globales et statiques sont stockées dans le .data
segment, et 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é aux 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 toujours dans le fichier .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 ()
{
;
}
En outre, envisager le programme suivant,
#include <stdio.h>
#include <stdlib.h>
int var[10]; /* Uninitialized so in .bss */
int main ()
{
var[0] = 20 /* **Initialized, where this 'var' will be ?** */
}
La raison est de réduire la taille du programme. Imaginez que votre programme C s'exécute sur un système incorporé, où le code et toutes les constantes sont sauvegardés dans true ROM (mémoire flash). Dans ces 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 ().
for(i=0; i<all_explicitly_initialized_objects; i++)
{
.data[i] = init_value[i];
}
memset(.bss,
0,
all_implicitly_initialized_objects);
Où .data et .bss sont stockés dans la RAM, mais init_value est stocké dans la ROM. Si c’était un segment, la taille de la ROM) devait être remplie de zéros, ce qui augmentait considérablement la taille 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 vraie ROM.
En outre, memset est probablement un assembleur en ligne très efficace, ce qui signifie que la copie au démarrage peut être exécutée plus rapidement.
Le segment .bss
Est une optimisation. Le segment entier .bss
Est décrit par un nombre unique, probablement 4 octets ou 8 octets, qui donne sa taille dans le processus en cours, alors que la section .data
Est aussi grande que la somme des tailles de les variables initialisées. Ainsi, le .bss
Rend les exécutables plus petits et plus rapides à charger. Sinon, les variables pourraient être dans le segment .data
Avec une initialisation explicite à zéro; 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 se trouvait dans le segment .data
.)
Dans le premier programme, a
serait dans le segment .data
Et b
serait dans le segment .bss
De l'exécutable. Une fois le programme chargé, la distinction devient immatérielle. Au moment de l'exécution, b
occupe 20 * sizeof(int)
octets.
Dans le second programme, var
se voit attribuer un espace et l'affectation dans main()
modifie cet espace. Il se trouve que l'espace pour var
a été décrit dans le segment .bss
Plutôt que dans le segment .data
, Mais cela n'affecte pas le comportement du programme lors de son exécution.
À partir de langage pas à pas pour l'assemblage: programmer avec Linux par Jeff Duntemann, en ce qui concerne la section . Data :
La section . Data contient les définitions de données d'éléments de données initialisés. Les données initialisées sont des données qui ont une valeur avant que le programme ne commence à s'exécuter. Ces valeurs font partie du fichier exécutable. Ils sont chargés en mémoire lorsque le fichier exécutable est chargé en mémoire pour exécution.
La chose importante à retenir à propos de la section .data est que plus vous définissez d'éléments de données initialisés, plus le fichier exécutable sera volumineux et plus le chargement de la mémoire à partir du disque dur sera long.
et la section . bss :
Tous les éléments de données n'ont pas besoin de valeurs avant que le programme ne commence à s'exécuter. Lorsque vous lisez des données d’un fichier sur un disque, par exemple, vous devez disposer d’un emplacement pour les données après leur entrée sur le disque. Les tampons de données de ce type sont définis dans la section . Bss de votre programme. Vous mettez de côté un certain nombre d’octets pour un tampon et lui donnez un nom, mais vous ne dites pas quelles valeurs doivent être présentes dans le tampon.
Il existe une différence cruciale entre les éléments de données définis dans la section .data et les éléments de données définis dans la section .bss: les éléments de données de la section .data s'ajoutent à la taille de votre fichier exécutable. Les éléments de données dans la section .bss ne le font pas. Un tampon qui occupe 16 000 octets (ou plus, parfois beaucoup plus) peut être défini dans .bss et n’ajoute presque rien (environ 50 octets pour la description) à la taille du fichier exécutable.
Eh bien, tout d’abord, les variables de votre exemple ne sont pas non initialisées; C spécifie que les variables statiques non initialisées sont initialisées à 0.
Donc, la raison d'être de .bss est d'avoir des exécutables plus petits, un gain de place et un chargement plus rapide du programme, car le chargeur ne peut allouer qu'un tas de zéros au lieu de copier les données à partir du disque.
Lors de l'exécution du programme, le programme de chargement charge les fichiers .data et .bss en mémoire. Les écritures dans les objets résidant en .data ou .bss ne vont donc que dans la mémoire, elles ne sont pas vidées à la binaire sur le disque à aucun moment.
L'article de Wikipédia . Bss fournit une explication historique intéressante, étant donné que le terme date du milieu des années 1950 (yippee mon anniversaire ;-).
À l’époque, tout était précieux, de sorte que toute méthode de signalisation des espaces vides réservés était utile. Ceci (. Bss ) est celui qui est bloqué.
. Les sections data correspondent à des espaces non vides. Elles contiennent plutôt vos valeurs définies.
Le System V ABI 4.1 (1997) (spécification AKA ELF) contient également la réponse:
.bss
Cette section contient les données non initialisées qui contribuent à l’image mémoire du programme. Par définition, le système initialise les données avec des zéros lorsque le programme commence à s'exécuter. La section n'occupe aucun espace de fichier, comme indiqué par le type de section,SHT_NOBITS
.
dit que le nom de la section .bss
est réservé et a des effets spéciaux, en particulier il n’occupe aucun espace de fichier , d’où l’avantage par rapport à .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 constitue un cas d'utilisation courant et fonctionne correctement pour les variables non initialisées.
Le SHT_NOBITS
La documentation de type de section répète cette affirmation:
sh_size
Ce membre donne la taille de la section en octets. À moins que le type de section ne soitSHT_NOBITS
, la section occupesh_size
octets dans le fichier. Une section de typeSHT_NOBITS
peut avoir une taille non nulle, mais elle n’occupe aucun espace dans le fichier.
La norme C ne dit rien sur les sections, mais nous pouvons facilement vérifier où la variable est stockée dans Linux avec objdump
et readelf
, et en conclure que des globales non initialisées sont en fait stockées dans le fichier .bss
, voir par exemple cette réponse: https://stackoverflow.com/a/36725211/895245