Зачем нужен сегмент .bss? - PullRequest
104 голосов
/ 02 марта 2012

Я знаю, что глобальные и статические переменные хранятся в сегменте .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 ?** */
}

Ответы [ 6 ]

78 голосов
/ 02 марта 2012

Причина заключается в уменьшении размера программы. Представьте, что ваша программа на 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 хранятся в RAM, а init_value хранится в ROM. Если это был один сегмент, то ПЗУ должно было заполняться множеством нулей, что значительно увеличивало размер ПЗУ.

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

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

74 голосов
/ 02 марта 2012

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

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

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

11 голосов
/ 31 мая 2014

Из Язык ассемблера. Пошаговое руководство: программирование под Linux Джеффа Дюнтеманна, в отношении раздела .data :

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

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

и .bss раздел:

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

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

9 голосов
/ 02 марта 2012

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

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

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

3 голосов

Система V 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, см., Например, этот ответ: https://stackoverflow.com/a/36725211/895245

3 голосов
/ 20 июля 2014

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...