Почему нужен сегмент .bss?

Я знаю, что глобальные и статические переменные хранятся в сегменте .data, а неинициализированные данные находятся в сегменте .bss. Я не понимаю, почему у нас есть выделенный сегмент для неинициализированных переменных? Если неинициализированная переменная имеет значение, назначенное во время выполнения, существует ли переменная все еще в сегменте .bss?

В следующей программе a находится в сегменте .data, а b - в сегменте .bss; это верно? Пожалуйста, поправьте меня, если мое понимание неверно.

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

Кроме того, рассмотрите следующую программу,

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

Ответ 1

Причина заключается в уменьшении размера программы. Представьте, что ваша программа C работает во встроенной системе, где код и все константы сохраняются в истинном ПЗУ (флэш-память). В таких системах необходимо выполнить начальное "копирование" для установки всех объектов статической хранения, прежде чем вызывается main(). Обычно это будет выглядеть как псевдо:

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

memset(.bss, 
       0, 
       all_implicitly_initialized_objects);

Где .data и .bss хранятся в ОЗУ, но значение init_value сохраняется в ПЗУ. Если бы это был один сегмент, тогда ПЗУ пришлось заполнить большим количеством нулей, значительно увеличив размер ПЗУ.

Исполняемые файлы на основе RAM работают аналогично, хотя, конечно, они не имеют истинного ПЗУ.

Кроме того, memset, скорее всего, является очень эффективным встроенным ассемблером, что означает, что запуск копирования может выполняться быстрее.

Ответ 2

Сегмент .bss - это оптимизация. Весь сегмент .bss описывается одним номером, возможно 4 байта или 8 байтов, который дает свой размер в текущем процессе, тогда как секция .data имеет размер, равный сумме размеров инициализированных переменных. Таким образом, .bss делает исполняемые файлы меньше и быстрее загружается. В противном случае переменные могут находиться в сегменте .data с явной инициализацией до нулей; программе было бы трудно сказать разницу. (Подробно, адрес объектов в .bss, вероятно, будет отличаться от адреса, если он был в сегменте .data.)

В первой программе a будет находиться в сегменте .data, а b будет в сегменте .bss исполняемого файла. После загрузки программы различие становится несущественным. Во время выполнения b занимает 20 * sizeof(int) байты.

Во второй программе var выделяется пространство, а присваивание в main() изменяет это пространство. Так получилось, что пространство для var было описано в сегменте .bss, а не в сегменте .data, но это не влияет на поведение программы при запуске.

Ответ 3

Ну, во-первых, эти переменные в вашем примере не являются неинициализированными; C указывает, что статические переменные, не инициализированные иначе, инициализируются значением 0.

Таким образом, причина для .bss состоит в том, чтобы иметь меньшие исполняемые файлы, экономя пространство и позволяя более быструю загрузку программы, так как загрузчик может просто выделить кучу нулей вместо копирования данных с диска.

При запуске программы загрузчик программ загрузит .data и .bss в память. Записывает объекты, находящиеся в .data или .bss, таким образом, только переходит в память, они не очищаются в двоичном виде на диске в любой точке.

Ответ 4

От "Шаг за шагом: программирование в Linux" Джеффом Дантеманом в отношении раздела .data​​strong >

Раздел .data​​strong > содержит определения данных инициализированных элементов данных. Initialized данные - это данные, которые имеют значение перед запуском программы. Эти значения являются частью исполняемого файла. Они загружаются в память, когда исполняемый файл загружается в память для выполнения.

Важно помнить о разделе .data, что более инициализированные элементы данных, которые вы определяете, чем больше исполняемый файл будет, и чем дольше это потребуется, чтобы загрузить его с диска в память когда вы запустите его.

и раздел .bss:

Не все элементы данных должны иметь значения перед запуском программы. Например, когда вы читаете данные из файла диска, вам нужно иметь место для ввода данных после того, как оно поступит с диска. Подобные буферы данных определенном в разделе .bss вашей программы. Вы отложите некоторое количество байтов для буфера и дать буферу имя, но вы не скажете, какие значения должны присутствовать в буфере.

Это важная разница между элементами данных, определенными в .data раздел и элементы данных, определенные в разделе .bss: элементы данных в .data добавьте размер исполняемого файла. Элементы данных в в разделе .bss нет. Буфер, который занимает 16 000 байт (или более, иногда намного больше) можно определить в .bss и добавить почти ничего (около 50 байт для описания) к размеру исполняемого файла.

Ответ 5

Статья в wikipedia .bss дает хорошее историческое объяснение, учитывая, что этот термин относится к середине 1950 года (yippee мой день рождения; -).

В тот же день каждый бит был драгоценным, поэтому был полезен любой способ сигнализации зарезервированного пустого пространства. Это (.bss) - это тот, который застрял.

.data​​strong > разделы предназначены для пространства, которое не пустое, скорее оно будет иметь (ваши) определенные значения, введенные в него.

Ответ 6

System V ABI 4.1 (1997) (спецификация AKA ELF) также содержит ответ:

.bss В этом разделе содержатся неинициализированные данные, которые программный образ памяти. По определению система инициализирует данные с нулями при запуске программы. Раздел не занимает файлового пространства, как указано типом раздела, SHT_NOBITS.

говорит, что имя раздела .bss зарезервировано и имеет специальные эффекты, в частности он не занимает файлового пространства, поэтому преимущество над .data.

Недостатком является то, что все байты должны быть установлены на 0, когда ОС помещает их в память, что является более ограничительным, но является обычным прецедентом и отлично работает для неинициализированных переменных.

Документация типа раздела SHT_NOBITS повторяет это утверждение:

sh_size Этот член дает размер секций в байтах. Если сек- тип SHT_NOBITS, секция занимает sh_sizeбайтов в файле. Раздел типа SHT_NOBITS может иметь ненулевой размер, но он не занимает места в файле.

В стандарте C ничего не говорится о разделах, но мы можем легко проверить, где переменная хранится в Linux с objdump и readelf, и заключить, что неинициализированные глобальные таблицы фактически хранятся в .bss, см., например этот ответ: fooobar.com/questions/9434/...

Ответ 7

Я не эксперт по операционным системам и проблемам управления памятью, но я думаю, что вы все перепутали: сегменты существуют только в программах (т.е. в исполняемых файлах). Помните, что программы являются пассивными сущностями, которые существуют и что все они ничего не делают.

Следовательно, нет смысла спрашивать, что происходит с униализованными переменными, хранящимися в сегментах .bss после их инициализации: как только программа загружается в память компьютера и начинает работать, она становится процессом (активным объектом что... Вы знаете, это бежит!).

В (возможно, виртуальном) пространстве памяти, предназначенном для этого процесса, у вас не будет сегментов, только стек, текст/код и области кучи (более поздний используется для выделения переменных). В этом отношении, пожалуйста, проверьте ответ, предоставленный Матс Петерсоном здесь.

Привет из Бразилии!