Dlaczego wymagany jest segment .bss?

120

Wiem tylko, że zmienne globalne i statyczne są przechowywane w .datasegmencie, a niezainicjowane dane znajdują się w .bsssegmencie. Nie rozumiem, dlaczego mamy dedykowany segment dla niezainicjowanych zmiennych? Jeśli niezainicjalizowana zmienna ma przypisaną wartość w czasie wykonywania, czy ta zmienna nadal istnieje tylko w .bsssegmencie?

W następnym programie ajest w .datasegmencie i bjest w .bsssegmencie; czy to jest poprawne? Uprzejmie popraw mnie, jeśli źle rozumiem.

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

Weź również pod uwagę następujący program,

#include <stdio.h>
#include <stdlib.h>
int var[10];  /* Uninitialized so in .bss */
int main ()
{
   var[0] = 20  /* **Initialized, where this 'var' will be ?** */
}
Kim jestem
źródło
3
Możesz przeczytać BSS jako Better Save Space .
smwikipedia

Odpowiedzi:

89

Powodem jest zmniejszenie rozmiaru programu. Wyobraź sobie, że Twój program C działa w systemie wbudowanym, gdzie kod i wszystkie stałe są zapisywane w prawdziwej pamięci ROM (pamięć flash). W takich systemach, przed wywołaniem metody main (), musi zostać wykonane wstępne kopiowanie w celu ustawienia wszystkich statycznych obiektów czasu przechowywania. Zwykle będzie wyglądać tak, jak to pseudo:

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

memset(.bss, 
       0, 
       all_implicitly_initialized_objects);

Gdzie .data i .bss są przechowywane w pamięci RAM, ale init_value jest przechowywany w ROM. Gdyby był to jeden segment, wówczas ROM musiałby być wypełniony wieloma zerami, co znacznie zwiększyło rozmiar ROM.

Pliki wykonywalne oparte na pamięci RAM działają podobnie, chociaż oczywiście nie mają prawdziwej pamięci ROM.

Ponadto memset jest prawdopodobnie bardzo wydajnym asemblerem wbudowanym, co oznacza, że ​​kopiowanie podczas uruchamiania może być wykonywane szybciej.

Lundin
źródło
7
Dla wyjaśnienia: jedyna różnica między .data i .bss polega na tym, że podczas uruchamiania „kopiowanie w dół” może być wykonywane sekwencyjnie, a więc szybciej. Gdyby nie został podzielony na dwa segmenty, inicjalizacja musiałaby pominąć miejsca w pamięci RAM należące do niezainicjowanych zmiennych, a więc marnować czas.
CL22
80

.bssSegment jest optymalizacja. Cały .bsssegment jest opisany pojedynczą liczbą, prawdopodobnie 4 bajty lub 8 bajtów, która określa jego rozmiar w działającym procesie, podczas gdy .datasekcja jest tak duża, jak suma rozmiarów zainicjalizowanych zmiennych. W ten sposób .bsssprawia, że ​​pliki wykonywalne są mniejsze i szybciej się ładują. W przeciwnym razie zmienne mogłyby znajdować się w .datasegmencie z jawną inicjalizacją zerową; programowi trudno byłoby stwierdzić różnicę. (W szczególności adres obiektów w programie .bssprawdopodobnie byłby inny niż adres, gdyby znajdował się w .datasegmencie).

W pierwszym programie aznajdowałby się w .datasegmencie i bbyłby w .bsssegmencie pliku wykonywalnego. Po załadowaniu programu rozróżnienie staje się nieistotne. W czasie wykonywania bzajmuje 20 * sizeof(int)bajty.

W drugim programie varjest przydzielana przestrzeń, a przypisanie w main()modyfikuje tę przestrzeń. Zdarza się, że miejsce varzostało opisane w .bsssegmencie, a nie w .datasegmencie, ale nie ma to wpływu na zachowanie programu podczas działania.

Jonathan Leffler
źródło
16
Na przykład rozważmy posiadanie wielu niezainicjowanych buforów o długości 4096 bajtów. Czy chciałbyś, aby wszystkie te bufory 4k przyczyniły się do rozmiaru pliku binarnego? To byłoby dużo zmarnowanej przestrzeni.
Jeff Mercado,
1
@jonathen killer: Dlaczego cały segment BSS jest opisywany pojedynczą liczbą?
Suraj Jain
@JonathanLeffler Mam na myśli, że wszystkie zmienne statyczne zainicjowane przez zero trafiają do bss. Czy więc jego wartość nie powinna wynosić zero? I dlaczego nie ma miejsca w sekcji .data, jak to zrobić, aby to spowolnić?
Suraj Jain
2
@SurajJain: przechowywana liczba to liczba bajtów, które mają być wypełnione zerami. O ile nie ma takich niezainicjowanych zmiennych, długość sekcji bss nie będzie wynosić zero, mimo że wszystkie bajty w sekcji bss będą równe zero po załadowaniu programu.
Jonathan Leffler
1
Sekcja .bss w pliku wykonywalnym to po prostu liczba. Sekcja .bss w obrazie procesu w pamięci jest zwykle pamięcią sąsiadującą z sekcją .data i często sekcja .data środowiska uruchomieniowego jest połączona z sekcją .bss; nie ma żadnego rozróżnienia w pamięci wykonawczej. Czasami można znaleźć miejsce rozpoczęcia bss ( edata). W praktyce, plik .bss nie istnieje w pamięci po zakończeniu obrazu procesu; wyzerowane dane są prostą częścią sekcji .data. Ale szczegóły różnią się w zależności od o / s itp.
Jonathan Leffler
15

Z języka asemblera krok po kroku: Programowanie w systemie Linux autorstwa Jeffa Duntemanna, w odniesieniu do sekcji .data :

Sekcja .data zawiera definicje danych zainicjowanych elementów danych. Zainicjowane dane to dane, które mają wartość przed rozpoczęciem działania programu. Te wartości są częścią pliku wykonywalnego. Są ładowane do pamięci, gdy plik wykonywalny jest ładowany do pamięci w celu wykonania.

Ważną rzeczą do zapamiętania w przypadku sekcji .data jest to, że im więcej zdefiniujesz zainicjowanych elementów danych, tym większy będzie plik wykonywalny i tym dłużej zajmie załadowanie go z dysku do pamięci po uruchomieniu.

i sekcja .bss :

Nie wszystkie elementy danych muszą mieć wartości przed rozpoczęciem działania programu. Na przykład podczas odczytywania danych z pliku na dysku musisz mieć miejsce na dane, które będą przechodzić po ich otrzymaniu z dysku. Takie bufory danych są zdefiniowane w sekcji .bss programu. Odkładasz pewną liczbę bajtów na bufor i nadajesz nazwę buforowi, ale nie mówisz, jakie wartości mają być obecne w buforze.

Istnieje zasadnicza różnica między elementami danych zdefiniowanymi w sekcji .data a elementami danych zdefiniowanymi w sekcji .bss: elementy danych w sekcji .data zwiększają rozmiar pliku wykonywalnego. Pozycje danych w sekcji .bss nie. Bufor, który zajmuje 16 000 bajtów (lub więcej, czasem znacznie więcej) może być zdefiniowany w .bss i prawie nic nie dodaje (około 50 bajtów na opis) do rozmiaru pliku wykonywalnego.

mihai
źródło
9

Po pierwsze, te zmienne w twoim przykładzie nie są niezainicjalizowane; C określa, że ​​zmienne statyczne, które nie zostały zainicjowane w inny sposób, są inicjowane na 0.

Dlatego też .bss ma mniejsze pliki wykonywalne, oszczędzając miejsce i umożliwiając szybsze ładowanie programu, ponieważ moduł ładujący może po prostu przydzielić kilka zer zamiast kopiować dane z dysku.

Podczas uruchamiania programu program ładujący załaduje pliki .data i .bss do pamięci. Zapisuje do obiektów znajdujących się w .data lub .bss, więc trafia tylko do pamięci, nie jest w żadnym momencie opróżniany do pliku binarnego na dysku.

janneb
źródło
5

Systemu ABI V 4.1 (1997) (znany jako ELF specyfikacja) zawiera również odpowiedź:

.bssTa sekcja zawiera niezainicjowane dane, które mają wpływ na obraz pamięci programu. Z definicji system inicjuje dane zerami, gdy program zaczyna działać. Sekcja nie zajmuje przestrzeni plików, jak wskazywany przez typ przekroju, SHT_NOBITS.

mówi, że nazwa sekcji .bssjest zarezerwowana i ma efekty specjalne, w szczególności nie zajmuje miejsca w pliku , co daje przewagę .data.

Wadą jest oczywiście to, że wszystkie bajty muszą być ustawione na, 0gdy system operacyjny umieszcza je w pamięci, co jest bardziej restrykcyjne, ale typowy przypadek użycia i działa dobrze dla niezainicjowanych zmiennych.

Dokumentacja SHT_NOBITStypu sekcji powtarza to stwierdzenie:

sh_sizeTen element członkowski podaje rozmiar sekcji w bajtach. O ile typ sekcji to nie jest SHT_NOBITS, sekcja zajmuje sh_size bajty w pliku. Sekcja typu SHT_NOBITSmoże mieć niezerowy rozmiar, ale nie zajmuje miejsca w pliku.

Standard C nie mówi nic o sekcjach, ale możemy łatwo sprawdzić, gdzie zmienna jest przechowywana w Linuksie za pomocą objdumpi readelf, i wyciągnąć wniosek, że niezainicjowane globalne są w rzeczywistości przechowywane w .bss. Zobacz na przykład tę odpowiedź: Co dzieje się z zadeklarowaną, niezainicjowaną zmienną w C?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
3

Artykuł na Wikipedii .bss dostarcza miłego wyjaśnienia historycznego, biorąc pod uwagę, że termin pochodzi z połowy lat pięćdziesiątych (yippee, moje urodziny ;-).

Kiedyś każdy bit był cenny, więc każda metoda sygnalizowania zarezerwowanego wolnego miejsca była przydatna. To ( .bss ) to ten, który utknął.

Sekcje .data są przeznaczone dla miejsca, które nie jest puste, a raczej będzie zawierało (twoje) zdefiniowane wartości wprowadzone do niego.

Philip Oakley
źródło