Зачем нужен сегмент .bss?


120

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

3
Вы можете прочитать BSS как лучше сэкономить место .
smwikipedia

Ответы:


89

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

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

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


7
Чтобы уточнить: единственная разница между .data и .bss заключается в том, что при запуске «копирование вниз» может выполняться последовательно, а значит, быстрее. Если бы он не был разделен на два сегмента, то при инициализации пришлось бы пропускать места в ОЗУ, принадлежащие неинициализированным переменным, что приводит к потере времени.
CL22

80

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

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

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


16
Например, рассмотрите возможность наличия многих неинициализированных буферов длиной 4096 байт. Хотели бы вы, чтобы все эти буферы 4k увеличивали размер двоичного файла? Это было бы много потраченного впустую места.
Джефф Меркадо

1
@jonathen killer: Почему весь сегмент bss описывается одним числом ??
Сурадж Джайн,

@JonathanLeffler Я имею в виду, что вся статическая переменная с нулевой инициализацией идет в bss. Разве его значение не должно быть просто нулем? А также почему им не дается место в разделе .data, как это может замедлить его?
Сурадж Джайн

2
@SurajJain: сохраненное число - это количество байтов, которые нужно заполнить нулями. Если таких неинициализированных переменных нет, длина секции bss не будет равна нулю, даже если все байты секции bss будут нулевыми после загрузки программы.
Джонатан Леффлер,

1
Раздел .bss в исполняемом файле - это просто число. Раздел .bss в образе процесса в памяти обычно является памятью, смежной с разделом .data, и часто раздел .data среды выполнения объединен с .bss; в оперативной памяти нет различий. Иногда вы можете найти, где начался bss ( edata). На практике после создания образа процесса .bss не существует в памяти; обнуленные данные являются простой частью раздела .data. Но детали различаются в зависимости от экипажа и т. Д.
Джонатан Леффлер,

15

Из Assembly Language Step-by-Step: Programming with Linux by Jeff Duntemann, относительно раздела .data :

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

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

и раздел .bss :

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

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


9

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

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

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


5

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. См., Например, этот ответ: Что происходит с объявленной неинициализированной переменной в C?


3

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.