¿Por qué se requiere el segmento .bss?

120

Lo que sé es que las variables globales y estáticas se almacenan en el .datasegmento y los datos no inicializados están en el .bsssegmento. Lo que no entiendo es por qué tenemos un segmento dedicado para las variables no inicializadas. Si una variable no inicializada tiene un valor asignado en tiempo de ejecución, ¿existe la variable .bsssolo en el segmento?

En el siguiente programa, aestá en el .datasegmento y bestá en el .bsssegmento; ¿Es eso correcto? Por favor corríjame si mi comprensión es incorrecta.

#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 ()
{
   ;
}  

Además, considere el siguiente programa,

#include <stdio.h>
#include <stdlib.h>
int var[10];  /* Uninitialized so in .bss */
int main ()
{
   var[0] = 20  /* **Initialized, where this 'var' will be ?** */
}
Quién soy
fuente
3
Puede leer BSS como Mejor ahorro de espacio .
smwikipedia

Respuestas:

89

La razón es reducir el tamaño del programa. Imagine que su programa C se ejecuta en un sistema integrado, donde el código y todas las constantes se guardan en una ROM real (memoria flash). En tales sistemas, se debe ejecutar una "copia abajo" inicial para establecer todos los objetos de duración de almacenamiento estático, antes de llamar a main (). Normalmente será así pseudo:

for(i=0; i<all_explicitly_initialized_objects; i++)
{
  .data[i] = init_value[i];
}

memset(.bss, 
       0, 
       all_implicitly_initialized_objects);

Donde .data y .bss se almacenan en RAM, pero init_value se almacena en ROM. Si hubiera sido un segmento, entonces la ROM tenía que llenarse con muchos ceros, aumentando significativamente el tamaño de la ROM.

Los ejecutables basados ​​en RAM funcionan de manera similar, aunque, por supuesto, no tienen una ROM real.

Además, Memset es probablemente un ensamblador en línea muy eficiente, lo que significa que la copia de inicio se puede ejecutar más rápido.

Lundin
fuente
7
Para aclarar: la única diferencia entre .data y .bss es que en el inicio, la "copia abajo" se puede ejecutar secuencialmente, por lo tanto, más rápido. Si no se dividiera en los dos segmentos, la inicialización tendría que omitir los puntos de RAM que pertenecen a las variables no inicializadas, perdiendo así tiempo.
CL22
80

El .bsssegmento es una optimización. Todo el .bsssegmento se describe con un solo número, probablemente 4 bytes u 8 bytes, que da su tamaño en el proceso en ejecución, mientras que la .datasección es tan grande como la suma de tamaños de las variables inicializadas. Por lo tanto, .bsshace que los ejecutables sean más pequeños y más rápidos de cargar. De lo contrario, las variables podrían estar en el .datasegmento con inicialización explícita a ceros; el programa tendría dificultades para notar la diferencia. (En detalle, la dirección de los objetos en .bssprobablemente sería diferente de la dirección si estuviera en el .datasegmento).

En el primer programa, aestaría en el .datasegmento y bestaría en el .bsssegmento del ejecutable. Una vez que se carga el programa, la distinción se vuelve irrelevante. En tiempo de ejecución, bocupa20 * sizeof(int) bytes.

En el segundo programa, varse asigna espacio y la asignación en main()modifica ese espacio. Sucede que el espacio para varse describió en el .bsssegmento en lugar del .datasegmento, pero eso no afecta la forma en que el programa se comporta cuando se ejecuta.

Jonathan Leffler
fuente
16
Por ejemplo, considere tener muchos búferes no inicializados de 4096 bytes de longitud. ¿Le gustaría que todos esos búferes de 4k contribuyan al tamaño del binario? Eso sería mucho espacio desperdiciado.
Jeff Mercado
1
@jonathen killer: ¿Por qué todo el segmento bss se describe con un solo número?
Suraj Jain
@JonathanLeffler Me refiero a que todas las variables estáticas inicializadas en cero van en bss. Entonces, ¿no debería ser su valor simplemente cero? Y también ¿por qué no se les da espacio en la sección .data? ¿Cómo puede hacerlo lento?
Suraj Jain
2
@SurajJain: el número almacenado es el número de bytes que se deben rellenar con ceros. A menos que no existan tales variables no inicializadas, la longitud de la sección bss no será cero, aunque todos los bytes de la sección bss serán cero una vez que se cargue el programa.
Jonathan Leffler
1
La sección .bss del ejecutable es simplemente un número. La sección .bss en la imagen del proceso en memoria es normalmente la memoria adyacente a la sección .data y, a menudo, la sección .data en tiempo de ejecución se combina con .bss; no se hace ninguna distinción en la memoria de ejecución. A veces, puede encontrar dónde comenzó el bss ( edata). En términos prácticos, el .bss no existe en la memoria una vez que se completa la imagen del proceso; los datos puestos a cero son parte simple de la sección .data. Pero los detalles varían según el sistema operativo, etc.
Jonathan Leffler
15

Del lenguaje ensamblador paso a paso: programación con Linux por Jeff Duntemann, con respecto a la sección .data :

El .data sección contiene definiciones de datos de elementos de datos inicializados. Los datos inicializados son datos que tienen un valor antes de que el programa comience a ejecutarse. Estos valores son parte del archivo ejecutable. Se cargan en la memoria cuando el archivo ejecutable se carga en la memoria para su ejecución.

Lo importante que debe recordar acerca de la sección .data es que cuantos más elementos de datos inicializados defina, más grande será el archivo ejecutable y más tiempo llevará cargarlo desde el disco a la memoria cuando lo ejecute.

y la sección .bss :

No todos los elementos de datos necesitan tener valores antes de que el programa comience a ejecutarse. Cuando lee datos de un archivo de disco, por ejemplo, necesita tener un lugar para que los datos vayan después de que vengan del disco. Los búferes de datos como ese se definen en el .bss sección de su programa. Usted reserva una cierta cantidad de bytes para un búfer y le da un nombre, pero no dice qué valores estarán presentes en el búfer.

Existe una diferencia crucial entre los elementos de datos definidos en la sección .data y los elementos de datos definidos en la sección .bss: los elementos de datos en la sección .data se suman al tamaño de su archivo ejecutable. Los elementos de datos de la sección .bss no. Un búfer que ocupa 16.000 bytes (o más, a veces mucho más) se puede definir en .bss y no agregar casi nada (aproximadamente 50 bytes para la descripción) al tamaño del archivo ejecutable.

mihai
fuente
9

Bueno, primero que nada, esas variables en su ejemplo no están sin inicializar; C especifica que las variables estáticas que no se inicializan de otro modo se inicializan en 0.

Entonces, la razón de .bss es tener ejecutables más pequeños, lo que ahorra espacio y permite una carga más rápida del programa, ya que el cargador puede asignar un montón de ceros en lugar de tener que copiar los datos del disco.

Al ejecutar el programa, el cargador de programas cargará .data y .bss en la memoria. Escribe en objetos que residen en .data o .bss, por lo que solo van a la memoria, no se descargan en el binario del disco en ningún momento.

Janneb
fuente
5

El System V ABI 4.1 (1997) (especificación AKA ELF) también contiene la respuesta:

.bssEsta sección contiene datos no inicializados que contribuyen a la imagen de memoria del programa. Por definición, el sistema inicializa los datos con ceros cuando el programa comienza a ejecutarse. La sección ocupa ningún espacio de archivos, como lo indica el tipo de sección, SHT_NOBITS.

dice que el nombre de la sección .bssestá reservado y tiene efectos especiales, en particular no ocupa espacio de archivo , de ahí la ventaja .data.

La desventaja es, por supuesto, que todos los bytes deben configurarse 0cuando el sistema operativo los coloca en la memoria, que es más restrictivo, pero es un caso de uso común, y funciona bien para las variables no inicializadas.

La SHT_NOBITSdocumentación tipo sección repite esa afirmación:

sh_sizeEste miembro da el tamaño de la sección en bytes. A menos que el tipo de sección sea SHT_NOBITS, la sección ocupa sh_size bytes en el archivo. Una sección de tipo SHT_NOBITSpuede tener un tamaño distinto de cero, pero no ocupa espacio en el archivo.

El estándar C no dice nada sobre las secciones, pero podemos verificar fácilmente dónde se almacena la variable en Linux con objdumpy readelf, y concluir que los globales no inicializados se almacenan de hecho en el archivo .bss. Vea, por ejemplo, esta respuesta: ¿Qué sucede con una variable declarada y no inicializada en C?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
3

El artículo de wikipedia .bss proporciona una buena explicación histórica, dado que el término es de mediados de la década de 1950 (yippee my birthday ;-).

En el pasado, todo era valioso, por lo que cualquier método para señalar un espacio vacío reservado era útil. Este ( .bss ) es el que se ha atascado.

Las secciones .data son para espacios que no están vacíos, sino que tendrán (sus) valores definidos ingresados ​​en él.

Philip Oakley
fuente