Linux下C程式的儲存空間布局

2021-09-07 18:28:46 字數 3082 閱讀 5879

在elf格式的可執行檔案中,全域性記憶體包括三種:bss、data和rodata。其它可執行檔案格式與之類似。了解了這三種資料的特點,我們才能充分發揮它們的長處,達到速度與空間的最優化。

1、bss(block started by symbol)

bss是指那些沒有初始化的和初始化為0的全域性變數和靜態變數,bss型別的全域性變數只佔執行時的記憶體空間,而不佔檔案空間。

另外,大多數作業系統,在引導程式時,會把所有的bss全域性變數全部清零,無需要你手工去清零。

但為保證程式的可移植性,手工把這些變數初始化為0也是乙個好習慣。

2、data

與bss相比,data就容易明白多了,它的名字就暗示著裡面存放著資料。當然,如果資料全是零,為了優化考慮,編譯器把它當作bss處理。通俗的說,data指那些初始化過(非零)的非const的全域性變數和靜態變數。

由此可見,data型別的全域性變數是即佔檔案空間,又占用執行時記憶體空間的。

3、rodata

rodata的意義同樣明顯,ro代表read only,即唯讀資料(const)。唯讀資料段,存放常量,字元常量,const常量,據說還存放除錯資訊。關於rodata型別的資料,要注意以下幾點:

常量不一定就放在rodata裡,有的立即數直接編碼在指令裡,存放在**段(.text)中。

對於字串常量,編譯器會自動去掉重複的字串,保證乙個字串在乙個可執行檔案(exe/so)中只存在乙份拷貝。

rodata是在多個程序間是共享的,這可以提高空間利用率。

在有的嵌入式系統中,rodata放在rom(如norflash)裡,執行時直接讀取rom記憶體,無需要載入到ram記憶體中。

在嵌入式linux系統中,通過一種叫作xip(就地執行)的技術,也可以直接讀取,而無需要載入到ram記憶體中。

由此可見,把在執行過程中不會改變的資料設為rodata型別的,是有很多好處的:在多個程序間共享,可以大大提高空間利用率,甚至不占用ram空間。同時由於rodata在唯讀的記憶體頁面(page)中,是受保護的,任何試圖對它的修改都會被及時發現,這可以幫助提高程式的穩定性。

4、text

通常是指用來存放程式執行**的一塊記憶體區域。這部分區域的大小在程式執行前就已經確定,並且記憶體區域通常屬於唯讀, 某些架構也允許**段為可寫,即允許修改程式。在**段中,也有可能包含一些唯讀的常數變數,例如字串常量等。

5、變數與關鍵字

static關鍵字用途太多,以致於讓新手模糊。不過,總結起來就有兩種作用,改變生命期和限制作用域。如:

修飾inline函式:限制作用域

修飾普通函式:限制作用域

修飾區域性變數:改變生命期

修飾全域性變數:限制作用域

const 關鍵字倒是比較明了,用const修飾的變數放在rodata裡,字串預設就是常量。對const,注意以下幾點就行了。

指標常量:指向的資料是常量。如 const char* p = 「abc」; p指向的內容是常量 ,但p本身不是常量,你可以讓p再指向」123」。

常量指標:指標本身是常量。如:char* const p = 「abc」; p本身就是常量,你不能讓p再指向」123」。

指標常量 + 常量指標:指標和指標指向的資料都是常量。const char* const p =」abc」; 兩者都是常量,不能再修改。

violatile關鍵字通常用來修飾多執行緒共享的全域性變數和io記憶體。告訴編譯器,不要把此類變數優化到暫存器中,每次都要老老實實的從記憶體中讀取,因為它們隨時都可能變化。這個關鍵字可能比較生僻,但千萬不要忘了它,否則乙個錯誤讓你除錯好幾天也得不到一點線索。

乙個程式本質上都是由 bss 段、data段、text段三個組成的。可以看到乙個可執行程式在儲存(沒有調入記憶體)時分為**段、資料區和未初始化資料區三部分。

程式編譯後生成的目標檔案至少含有這三個段,這三個段的大致結構圖如下所示:

text段和data段在編譯時已經分配了空間,而bss段並不占用可執行檔案的大小,它是由鏈結器來獲取記憶體的

bss段(未進行初始化的資料)的內容並不存放在磁碟上的程式檔案中。其原因是核心在程式開始執行前將它們設定為0。需要存放在程式檔案中的只有正文段和初始化資料段。

data段(已經初始化的資料)則為資料分配空間,資料儲存到目標檔案中。

資料段包含經過初始化的全域性變數以及它們的值。bss段的大小從可執行檔案中得到,然後鏈結器得到這個大小的記憶體塊,緊跟在資料段的後面。當這個記憶體進入程式的位址空間後全部清零。包含資料段和bss段的整個區段此時通常稱為資料區。

可執行程式在執行時又多出兩個區域:棧區和堆區。

(4)棧區:由編譯器自動釋放,存放函式的引數值、區域性變數等。每當乙個函式被呼叫時,該函式的返回型別和一些呼叫的資訊被存放到棧中。然後這個被呼叫的 函式再為他的自動變數和臨時變數在棧上分配空間。每呼叫乙個函式乙個新的棧就會被使用。棧區是從高位址位向低位址位增長的,是一塊連續的記憶體區域,最大容 量是由系統預先定義好的,申請的棧空間超過這個界限時會提示溢位,使用者能從棧中獲取的空間較小。

(5)堆區:用於動態分配記憶體,位於bss和棧中間的位址區域。由程式設計師申請分配和釋放。堆是從低位址位向高位址位增長,採用鏈式儲存結構。頻繁的 malloc/free造成記憶體空間的不連續,產生碎片。當申請堆空間時庫函式是按照一定的演算法搜尋可用的足夠大的空間。因此堆的效率比棧要低的多。

下圖將體現c的原始檔對應儲存空間:

此時程式還沒有被放入記憶體,只是在硬碟儲存的情況,此時bss並未占用空間。bss在鏈結的時候被獲得記憶體空間。

下圖表示程式執行,即程式在記憶體時的儲存布局:

int a = 0; //全域性初始化區  

char *p1; //全域性未初始化區

main()

Linux下C程式的儲存空間布局

下面是對可執行檔案aa執行size命令後得到的結果。root 1 algriom size aa text data bss dec hex filename 1257 500 16 1773 6ed aa 其中 text 表示正文段大小,這是cpu執行的機器指令部分,通常,正文段是可共享的,所以即...

Linux下C程式的儲存空間布局

下面是對可執行檔案aa執行size命令後得到的結果。root 1 algriom size aa text data bss dec hex filename 1257 500 16 1773 6ed aa 其中 text 表示正文段大小,這是cpu執行的機器指令部分,通常,正文段是可共享的,所以即...

Linux下C程式的儲存空間布局

乙個程式本質上都是由 bss 段 data段 text段三個組成的。可以看到乙個可執行程式在儲存 沒有調入記憶體 時分為 段 資料區和未初始化資料區三部分。程式編譯後生成的目標檔案至少含有這三個段,這三個段的大致結構圖如下所示 text段和data段在編譯時已經分配了空間,而bss段並不占用可執行檔...