(深入理解計算機系統) bss段,data段、text段、堆(heap)和棧(stack) 1

(深入理解計算機系統) bss段,data段、text段、堆(heap)和棧(stack) 1

關於BSS段的大小 2

1. BSS段中的內容 2

2.BSS段在加載運行前的處理 3

3.BSS段的作用 3

4. 代碼優化對BSS段的影響 3

5.Linux 下查看段屬性的指令: 4

BSS大小實驗 4

清除BSS段的一般做法 6

link腳本一般包含類似語句: 6

 

(深入理解計算機系統) bss段,data段、text段、堆(heap)和棧(stack)

一個程序本質上都是由bss段、data段、text段三個組成的。

bss段:

bss段屬於靜態內存分配。 bss是英文Block Started by Symbol的簡稱。

 BSS段通常是指用來存放程序中未初始化的全局變量和靜態變量的一塊內存區域。特點是可讀寫的,在程序執行之前BSS段會自動清0,所以,未初始的全局變量在程序執行之前已經成0了。

特點是:可讀寫的,

data段:

數據段(data segment)通常是指用來存放程序中已初始化的全局變量的一塊內存區域。

數據段屬於靜態內存分配。

text段:

代碼段(code segment/text segment)通常是指用來存放程序執行代碼的一塊內存區域。

這部分區域的大小在程序運行前就已經確定,並且內存區域通常屬於只讀(某些架構也允許代碼段爲可寫,即允許修改程序)。

在代碼段中,也有可能包含一些只讀的常數變量,例如字符串常量等。

堆(heap):

堆是用於存放進程運行中被動態分配的內存段,它的大小並不固定,可動態擴張或縮減。

當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);

當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)。

棧(stack):

棧又稱堆棧,是用戶存放程序臨時創建的局部變量,

也就是說我們函數括弧“{}”中定義的變量(但不包括static聲明的變量,static意味着在數據段中存放變量)。

除此以外,在函數被調用時,其參數也會被壓入發起調用的進程棧中,並且待到調用結束後,函數的返回值也會被存放回棧中。

由於棧的先進先出(FIFO)特點,所以棧特別方便用來保存/恢復調用現場。

從這個意義上講,我們可以把堆棧看成一個寄存、交換臨時數據的內存區。

 

 

 

 

關於BSS段的大小

BSS段在執行文件中時候不佔磁盤空間,要運行的時候才分配空間並清0.

歷史原因

而很多時候,數據段裏的全局變量都是0(或者沒有初始值),那麼存儲這麼多的0到目標文件(可執行)裏其實是沒有必要的。所以爲了節約空間,在生成目標文件的時候,就把沒有初始值(實際就是0)的數據段裏的變量都放到BSS段裏,這樣目標文件就不需要那麼大的體積裏(節約磁盤空間)。只有當目標文件被載入(運行)的時候,加載器負責把BSS段清零(一個循環就可以搞定)。 之後,這個規則慢慢的成爲一個標準配置,大多數編譯器也就都支持了BSS段。

  1. BSS段中的內容

先明確 BSS 段“存放”的是未初始化的全局變量與局部靜態變量,此處指的存放是指爲其預留空間(佔位符)。但BSS段在磁盤上不是真的佔用變量大小的空間,它僅是在該段中記錄了所有未初始化全局變量與局部靜態變量的大小總和,至於每個變量的大小則存儲在符號表的size屬性中。即:

BSS段內容:無內容,它將在段表中佔一個段描述符,該段描述符的size屬性將記錄未初始化的全局變量與局部靜態變量的大小總和每個未初始化全局對象與靜態對象的大小:存儲在符號表的 size 屬性中

(上述說法求證於C++牛人“藍色”,表感謝。。。)

注:段表描述了ELF各個段的信息,比如每個段的段名,長度,在文件中的偏移等。ELF文件的段結構就是由段表決定的。

注:段表的結構是一個以“Elf32_shdr”結構體爲元素的數組,數組元素個人等於段的數目,每一個結構體對應一個段。將這個結構體稱爲段描述符。而BSS則僅在段表中佔用一個段描述符。但是在實際的ELF文件中不存在該段。

2.BSS段在加載運行前的處理

當可執行文件加載運行前,會爲BSS段中的變量分配足夠的空間並全部自動清理(因此,纔有未初始化的全局變量的值爲0的說法)。

3.BSS段的作用

BSS段主要是爲了節省可執行文件在磁盤上所佔的空間,其僅僅記錄變量所需的大小。對未初始化的大型數組的節省效率比較明顯。舉例如下:

1.static int a[10000];  

2.int main()  

3.{  

4.     //...  

5.}  

在上述程序中,若不存在 BSS 段,則可執行文件將開闢一個 10000 * sizeof(int) 大小的空間,並全部存儲爲0,int 爲4字節的情況下,該變量將在磁盤上佔用39KB的空間。但是此時若是存在BSS 段,則在可執行文件中,將只是記錄現在的BSS段總大小爲40000即可,而無需真正的佔據39KB的空間

該可執行文件在執行前將重新開闢39KB的空間,並自動初始化爲0

4. 代碼優化對BSS段的影響

全局變量與靜態變量沒有初始化或初始化值爲0時,都會放在.bss段。初始化爲非0值,則放在.data段。

 

考慮以下兩個靜態變量分別存儲在哪個段中:

  1. static int x1 = 1;  
  2. static int x2 = 0;  

很明顯可以看出,X1將被髮在.data段中。令人意外的是 X2 將被放置在 .bss 段中,因爲 x2 的值爲0,被認爲是未初始化的,因此將會被放在 .bss 段中以節省磁盤空間。

5.Linux 下查看段屬性的指令:

用readelf -s 或 objdump -t 查看符號表用readelf -S 或 objdump -h 查看段表

 牽涉到嵌入式系統運行時的內存大小分配,存儲單元佔用空間大小的問題。

在採用段式內存管理的架構中(比如intel的80x86系統),bss段通常是指用來存放程序中未初始化的全局變量的一塊內存區域,

 一般在初始化時bss段部分將會清零。bss段屬於靜態內存分配,即程序一開始就將其清零了。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BSS大小實驗

比如,在C語言之類的程序編譯完成之後,

  1. 已初始化的全局變量保存在.data段中,
  2. 未初始化的全局變量保存在.bss段中。

 

text和data段都在可執行文件中(在嵌入式系統裏一般是固化在鏡像文件中),由系統從可執行文件中加載;而bss段不在可執行文件中,由系統初始化

 

【例】

兩個小程序如下:

程序1:

int ar[30000];

void main()

{

......

}

程序2:

int ar[300000]={1,2,3,4,5,6};

void main()

{

......

}

發現程序2編譯之後所得的.exe文件比程序1的要大得多。

 

當下甚爲不解,於是手工編譯了一下,並使用了/FAs編譯選項來查看了一下其各自的.asm,

發現在程序1.asm中ar的定義如下:

_BSS SEGMENT

?ar@@3PAHA DD 0493e0H DUP(?);ar

_BSS ENDS

而在程序2.asm中,ar被定義爲:

_DATA SEGMENT

?ar@@3PAHA DD 01H;ar

DD 02H

DD 03H

ORG$+1199988

_DATA ENDS

區別很明顯,一個位於.bss段,而另一個位於.data段,兩者的區別在於:

全局的未初始化變量存在於.bss段中,具體體現爲一個佔位符;

全局的已初始化變量存於.data段中;

而函數內的自動變量都在棧上分配空間;

 

.bss是不佔用.exe文件空間的,其內容由操作系統初始化(清零);

.data卻需要佔用,其內容由程序初始化。因此造成了上述情況。

bss段(未手動初始化的數據)並不給該段的數據分配空間,只是記錄數據所需空間的大小;

bss段的大小從可執行文件中得到,然後鏈接器得到這個大小的內存塊,緊跟在數據段後面。

data段(已手動初始化的數據)則爲數據分配空間,數據保存在目標文件中;

data段包含經過初始化的全局變量以及它們的值。當這個內存區進入程序的地址空間後全部清零。

包含data段和bss段的整個區段此時通常稱爲數據區。

 

 

清除BSS段的一般做法

link腳本一般包含類似語句:

1

2

3

4

5

6

7

8

9

10

11

_bss_start = .;

__bss_start__ = .;

.bss            : 

{

  *(.shbss)

  *(.bss .bss.* .gnu.linkonce.b.*)

  *(COMMON)

}

. = ALIGN (4);

_bss_end = . ;

__bss_end__ = . ;

啓動過程的代碼一般在彙編做:

1

2

3

4

5

6

7

8

9

    @++++clear the BSS section++++

    ldr     r2,=__bss_start__

    ldr     r3,=__bss_end__

    mov     r12,#0

bss_loop:

    cmp     r2,r3 

    stmltia r2!,{r12}

    blt     bss_loop  

    @----clear the BSS section----

所以不要在C代碼中對未初始化的全局變量賦0,因爲BSS段會被自動賦0.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章