天天看點

bss、data和rodata差別與聯系 bss、data和rodata差別與聯系

bss、data和rodata差別與聯系

标簽: linux軟體連結位址 2013-01-22 09:19  7925人閱讀  評論(2)  收藏  舉報 有人可能會說,全局記憶體就是全局變量嘛,有必要專門一章來介紹嗎?這麼簡單的東西,還能玩出花來?我從來沒有深究它,不一樣寫程式嗎?關于全局記憶體這個主題雖然玩不出花來,但确實有些重要,了解這些知識,對于優化程式的時間和空間很有幫助。因為有好幾次這樣經曆,我才決定花一章篇幅來介紹它。

正如大家所知道的,全局變量是放在全局記憶體中的,但反過來卻未必成立。用static修飾的局部變量就是放在放全局記憶體的,它的作用域是局部的,但生命期是全局的。在有的嵌入式平台中,堆實際上就是一個全局變量,它占用相當大的一塊記憶體,在運作時,把這塊記憶體進行二次配置設定。

這裡我們并不強調全局變量和全局記憶體的差别。在本文中,全局強調的是它的生命期,而不是它的作用域,是以有時可能把兩者的概念互換。

一般來說,在一起定義的兩個全局變量,在記憶體的中位置是相鄰的。這是一個簡單的常識,但有時挺有用,如果一個全局變量被破壞了,不防先查查其前後相關變量的通路代碼,看看是否存在越界通路的可能。

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

1.         bss

已經記不清bss代表Block Storage Start還是Block Started by Symbol。像這我這種沒有用過那些史前計算機的人,終究無法明白這樣怪異的名字,也就記不住了。不過沒有關系,重要的是,我們要清楚bss全局變量有什麼樣特點,以及如何利用它。

通俗的說, bss是指那些沒有初始化的和初始化為0的全局變量。它有什麼特點呢,讓我們來看看一個小程式的表現。

int bss_array[1024 * 1024] = {0};

int main(int argc, char* argv[])

{

    return 0;

}

[[email protected] bss]# gcc -g bss.c -o bss.exe

[[email protected] bss]# ll

total 12

-rw-r--r-- 1 root root   84 Jun 22 14:32 bss.c

-rwxr-xr-x 1 root root 5683 Jun 22 14:32 bss.exe

變量bss_array的大小為4M,而可執行檔案的大小隻有5K。  由此可見,bss類型的全局變量隻占運作時的記憶體空間,而不占檔案空間。

另外,大多數作業系統,在加載程式時,會把所有的bss全局變量全部清零,無需要你手工去清零。但為保證程式的可移植性,手工把這些變量初始化為0也是一個好習慣。

2.         data

與bss相比,data就容易明白多了,它的名字就暗示着裡面存放着資料。當然 ,如果資料全是零,為了優化考慮,編譯器把它當作bss處理。通俗的說, data指那些初始化過(非零)的非const的全局變量。它有什麼特點呢,我們還是來看看一個小程式的表現。

int data_array[1024 * 1024] = {1};

int main(int argc, char* argv[])

{

    return 0;

}

[[email protected] data]# gcc -g data.c -o data.exe

[[email protected] data]# ll

total 4112

-rw-r--r-- 1 root root      85 Jun 22 14:35 data.c

-rwxr-xr-x 1 root root 4200025 Jun 22 14:35 data.exe

僅僅是把初始化的值改為非零了,檔案就變為4M多。 由此可見,data類型的全局變量是即占檔案空間,又占用運作時記憶體空間的。

3.         rodata

rodata的意義同樣明顯,ro代表 read only,即隻讀資料(const)。關于 rodata類型的資料,要注意以下幾點:

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

l         對于 字元串常量,編譯器會 自動去掉重複的字元串,保證一個字元串在一個可執行檔案(EXE/SO)中隻存在一份拷貝。

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

l         在有的嵌入式系統中, rodata放在ROM(如norflash)裡, 運作時直接讀取ROM記憶體,無需要加載到RAM記憶體中。

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

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

4.         變量與關鍵字

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

l         修飾inline函數:限制作用域

l         修飾普通函數:限制作用域

l         修飾局部變量:改變生命期

l         修飾全局變量:限制作用域

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

l          指針常量:指向的資料是常量。如  const char* p =  “abc”; p指向的内容是常量 ,但p本身不是常量,你可以讓p再指向”123”。

l         常量指針:指針本身是常量。如:char*  const  p = “abc”; p本身就是常量,你不能讓p再指向”123”。

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

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