web-dev-qa-db-fra.com

Pourquoi le segment .bss est-il requis?

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 ?** */
}
106
Whoami

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.

79
Lundin

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.

74
Jonathan Leffler

À 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.

11
mihai

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.

9
janneb

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.

3
Philip Oakley

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 soit SHT_NOBITS, la section occupe sh_size octets dans le fichier. Une section de type SHT_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