Чому потрібен сегмент .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 зберігаються в ОЗУ, але init_value зберігається в ПЗУ. Якщо це був один сегмент, то ПЗУ довелося заповнити великою кількістю нулів, значно збільшивши розмір ПЗУ.

Виконані файли на основі оперативної пам’яті працюють аналогічно, хоча, звичайно, у них немає справжнього ПЗУ.

Крім того, memset, ймовірно, є дуже ефективним вбудованим асемблером, що означає, що завантажувальна копія може бути виконана швидше.


7
Для уточнення: Єдина відмінність .data від .bss полягає в тому, що при запуску "копіювання" можна запускати послідовно, отже, швидше. Якби його не розділити на два сегменти, тоді при ініціалізації довелося б пропустити місця RAM, що належать до неініціалізованих змінних, тому витрачаючи час.
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 убивця: Чому весь сегмент bss описується одним числом ??
Сурай Джайн

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

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

1
Розділ .bss у виконуваному файлі - це просто число. Розділ .bss у зображенні процесу в пам'яті, як правило, є пам'яттю поруч із розділом .data, і часто розділ .data часу виконання комбінується із .bss; немає ніякої різниці, зробленої в пам’яті виконання. Іноді ви можете знайти, звідки почався bss ( edata). На практиці .bss не існує в пам'яті, коли зображення процесу завершено; нульові дані є простою частиною розділу .data. Але деталі змінюються залежно від о / с тощо
Джонатан Леффлер

15

З мови мовлення покроково: програмування за допомогою Джеффа Дантемана з Linux , щодо розділу .data :

Розділ .data містить визначення даних ініціалізованих елементів даних. Ініціалізовані дані - це дані, які мають значення до запуску програми. Ці значення є частиною виконуваного файлу. Вони завантажуються в пам'ять, коли виконуваний файл завантажується в пам'ять для виконання.

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

і .bss- розділ:

Не всі елементи даних повинні мати значення перед запуском програми. Наприклад, коли ви читаєте дані з дискового файлу, вам потрібно мати місце, де дані повинні надходити після того, як вони надходять з диска. Такі буфери даних визначені в розділі .bss вашої програми. Ви виділяєте деяку кількість байтів для буфера і даєте ім'я буфера, але ви не говорите, які значення мають бути присутніми в буфері.

Існує вирішальна різниця між елементами даних, визначеними в розділі .data, та елементами даних, визначеними в розділі .bss: елементи даних у розділі .data додають розмір виконуваного файлу. Елементи даних у розділі .bss не роблять. Буфер, що займає 16000 байт (або більше, іноді набагато більше), можна визначити в .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

Стаття у wikipedia .bss дає приємне історичне пояснення, враховуючи, що цей термін - з середини 1950-х (yippee - мій день народження ;-).

У той час кожен шматочок був дорогоцінним, тому будь-який метод сигналізації зарезервованого порожнього простору був корисним. Це ( .bss ) - той, що застряг.

розділи .data - це простір, який не є порожнім, скоріше він матиме (ваші) визначені значення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.