Wiem tylko, że zmienne globalne i statyczne są przechowywane w .data
segmencie, a niezainicjowane dane znajdują się w .bss
segmencie. 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 .bss
segmencie?
W następnym programie a
jest w .data
segmencie i b
jest w .bss
segmencie; 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 ?** */
}
c
linux
compiler-construction
Kim jestem
źródło
źródło
Odpowiedzi:
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:
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.
źródło
.bss
Segment jest optymalizacja. Cały.bss
segment jest opisany pojedynczą liczbą, prawdopodobnie 4 bajty lub 8 bajtów, która określa jego rozmiar w działającym procesie, podczas gdy.data
sekcja jest tak duża, jak suma rozmiarów zainicjalizowanych zmiennych. W ten sposób.bss
sprawia, że pliki wykonywalne są mniejsze i szybciej się ładują. W przeciwnym razie zmienne mogłyby znajdować się w.data
segmencie z jawną inicjalizacją zerową; programowi trudno byłoby stwierdzić różnicę. (W szczególności adres obiektów w programie.bss
prawdopodobnie byłby inny niż adres, gdyby znajdował się w.data
segmencie).W pierwszym programie
a
znajdowałby się w.data
segmencie ib
byłby w.bss
segmencie pliku wykonywalnego. Po załadowaniu programu rozróżnienie staje się nieistotne. W czasie wykonywaniab
zajmuje20 * sizeof(int)
bajty.W drugim programie
var
jest przydzielana przestrzeń, a przypisanie wmain()
modyfikuje tę przestrzeń. Zdarza się, że miejscevar
zostało opisane w.bss
segmencie, a nie w.data
segmencie, ale nie ma to wpływu na zachowanie programu podczas działania.źródło
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.Z języka asemblera krok po kroku: Programowanie w systemie Linux autorstwa Jeffa Duntemanna, w odniesieniu do sekcji .data :
i sekcja .bss :
źródło
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.
źródło
Systemu ABI V 4.1 (1997) (znany jako ELF specyfikacja) zawiera również odpowiedź:
mówi, że nazwa sekcji
.bss
jest 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,
0
gdy 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_NOBITS
typu sekcji powtarza to stwierdzenie:Standard C nie mówi nic o sekcjach, ale możemy łatwo sprawdzić, gdzie zmienna jest przechowywana w Linuksie za pomocą
objdump
ireadelf
, 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?źródło
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.
źródło