bss、data和rodata區別與聯絡

來源:互聯網
上載者:User

標籤:

有人可能會說,全域記憶體就是全域變數嘛,有必要專門一章來介紹嗎?這麼簡單的東西,還能玩出花來?我從來沒有深究它,不一樣寫程式嗎?關於全域記憶體這個主題雖然玩不出花來,但確實有些重要,瞭解這些知識,對於最佳化程式的時間和空間很有協助。因為有好幾次這樣經曆,我才決定花一章篇幅來介紹它。

正如大家所知道的,全域變數是放在全域記憶體中的,但反過來卻未必成立。用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記憶體。告訴編譯器,不要把此類變數最佳化到寄存器中,每次都要老老實實的從記憶體中讀取,因為它們隨時都可能變化。這個關鍵字可能比較生僻,但千萬不要忘了它,否則一個錯誤讓你調試好幾天也得不到一點線索。

bss、data和rodata區別與聯絡

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.