¿Por qué se requiere el segmento .bss?

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

En el siguiente progtwig, a está en el segmento .data , y b está en el segmento .bss ; ¿Es eso correcto? Amablemente corrígeme si mi comprensión es incorrecta.

 #include  #include  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 seguir el progtwig,

 #include  #include  int var[10]; /* Uninitialized so in .bss */ int main () { var[0] = 20 /* **Initialized, where this 'var' will be ?** */ } 

La razón es para reducir el tamaño del progtwig. Imagine que su progtwig C se ejecuta en un sistema integrado, donde el código y todas las constantes se guardan en una ROM verdadera (memoria flash). En dichos sistemas, se debe ejecutar una “copia de seguridad” inicial para establecer todos los objetos de duración de almacenamiento estático, antes de llamar a main (). Por lo general, irá como este pseudo:

 for(i=0; i 

Donde .data y .bss están almacenados en la RAM, pero init_value se almacena en la 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 verdadera.

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.

El segmento .bss es una optimización. Todo el segmento .bss 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 sección .data es tan grande como la sum de los tamaños de las variables inicializadas. Por lo tanto, el .bss hace los ejecutables más pequeños y más rápidos de cargar. De lo contrario, las variables podrían estar en el segmento .data con inicialización explícita a ceros; el progtwig estaría en apuros para notar la diferencia. (En detalle, la dirección de los objetos en .bss probablemente sería diferente de la dirección si estuviera en el segmento .data ).

En el primer progtwig, a estaría en el segmento .data b estaría en el segmento .bss del ejecutable. Una vez que se carga el progtwig, la distinción se vuelve inmaterial. En tiempo de ejecución, b ocupa 20 * sizeof(int) bytes.

En el segundo progtwig, var tiene asignado espacio y la asignación en main() modifica ese espacio. Sucede que el espacio para var se describió en el segmento .bss lugar del segmento .data , pero eso no afecta la forma en que se comporta el progtwig cuando se ejecuta.

Bueno, antes que nada, esas variables en su ejemplo no están sin inicializar; C especifica que las variables estáticas que no se hayan inicializado se inicializan a 0.

Así que la razón de .bss es tener ejecutables más pequeños, ahorrando espacio y permitiendo una carga más rápida del progtwig, ya que el cargador solo puede asignar un montón de ceros en lugar de tener que copiar los datos del disco.

Cuando se ejecuta el progtwig, el cargador de progtwig cargará .data y .bss en la memoria. Escribe en objetos que residen en .data o .bss, por lo tanto, solo van a la memoria, no se vacían al binario en el disco en ningún punto.

De Assembly Language Step-by-Step: Progtwigción con Linux por Jeff Duntemann, con respecto a la sección .data :

La sección .data contiene definiciones de datos de elementos de datos inicializados. Los datos inicializados son datos que tienen un valor antes de que el progtwig 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 cuanto más elementos de datos inicializados defina, mayor será el archivo ejecutable y más tiempo tardará en cargarlo desde el disco en la memoria cuando lo ejecute.

y la sección .bss :

No todos los elementos de datos deben tener valores antes de que el progtwig comience a ejecutarse. Cuando está leyendo datos de un archivo de disco, por ejemplo, debe tener un lugar donde ubicar los datos después de que provienen del disco. Los búferes de datos como ese se definen en la sección .bss de su progtwig. Se reserva un número de bytes para un búfer y se le asigna un nombre al búfer, pero no se dice qué valores deben estar 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 agregan al tamaño del archivo ejecutable. Los elementos de datos en 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 agregar casi nada (aproximadamente 50 bytes para la descripción) al tamaño del archivo ejecutable.

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, mi cumpleaños ;-).

De vuelta en el día, cada bit era precioso, por lo que cualquier método para señalizar el espacio vacío reservado, fue útil. Este ( .bss ) es el que se ha estancado.

Las secciones de datos son para espacio que no está vacío, sino que tendrá (su) valores definidos ingresados ​​en él.

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

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

dice que el nombre de la sección .bss está reservado y tiene efectos especiales, en particular, no ocupa espacio de archivos , por lo que tiene la ventaja sobre .data .

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

La documentación del tipo de sección SHT_NOBITS repite esa afirmación:

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

El estándar C no dice nada sobre secciones, pero podemos verificar fácilmente dónde se almacena la variable en Linux con objdump y readelf , y concluir que los globales sin inicializar se almacenan en el .bss , ver por ejemplo esta respuesta: https: // stackoverflow .com / a / 36725211/895245