data,bss和rodata段的区别与联系
正如大家所知道的,全局變量是放在全局內(nèi)存中的,但反過來(lái)卻未必成立。用static修飾的局部變量就是放在放全局內(nèi)存的,它的作用域是局部的,但生命期是全局的。在有的嵌入式平臺(tái)中,堆實(shí)際上就是一個(gè)全局變量,它占用相當(dāng)大的一塊內(nèi)存,在運(yùn)行時(shí),把這塊內(nèi)存進(jìn)行二次分配。
這里我們并不強(qiáng)調(diào)全局變量和全局內(nèi)存的差別。在本文中,全局強(qiáng)調(diào)的是它的生命期,而不是它的作用域,所以有時(shí)可能把兩者的概念互換。
一般來(lái)說(shuō),在一起定義的兩個(gè)全局變量,在內(nèi)存的中位置是相鄰的。這是一個(gè)簡(jiǎn)單的常識(shí),但有時(shí)挺有用,如果一個(gè)全局變量被破壞了,不防先查查其前后相關(guān)變量的訪問代碼,看看是否存在越界訪問的可能。
在ELF格式的可執(zhí)行文件中,全局內(nèi)存包括三種:bss、data和rodata。其它可執(zhí)行文件格式與之類似。了解了這三種數(shù)據(jù)的特點(diǎn),我們才能充分發(fā)揮它們的長(zhǎng)處,達(dá)到速度與空間的最優(yōu)化。
1、bss?
已經(jīng)記不清bss代表Block Storage Start還是Block Started by Symbol。像這我這種沒有用過那些史前計(jì)算機(jī)的人,終究無(wú)法明白這樣怪異的名字,也就記不住了。不過沒有關(guān)系,重要的是,我們要清楚bss全局變量有什么樣特點(diǎn),以及如何利用它。
通俗的說(shuō),bss是指那些沒有初始化的和初始化為0的全局變量。它有什么特點(diǎn)呢,讓我們來(lái)看看一個(gè)小程序的表現(xiàn)。?
int bss_array[1024 * 1024] = {0};int main(int argc, char* argv[])? {? return 0;? }? [root@localhost bss]# gcc -g bss.c -o bss.exe? [root@localhost 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,而可執(zhí)行文件的大小只有5K。 由此可見,bss類型的全局變量只占運(yùn)行時(shí)的內(nèi)存空間,而不占文件空間。
另外,大多數(shù)操作系統(tǒng),在加載程序時(shí),會(huì)把所有的bss全局變量全部清零,無(wú)需要你手工去清零。但為保證程序的可移植性,手工把這些變量初始化為0也是一個(gè)好習(xí)慣。
2、data?
與bss相比,data就容易明白多了,它的名字就暗示著里面存放著數(shù)據(jù)。當(dāng)然,如果數(shù)據(jù)全是零,為了優(yōu)化考慮,編譯器把它當(dāng)作bss處理。通俗的說(shuō),data指那些初始化過(非零)的非const的全局變量。它有什么特點(diǎn)呢,我們還是來(lái)看看一個(gè)小程序的表現(xiàn)。?
僅僅是把初始化的值改為非零了,文件就變?yōu)?M多。由此可見,data類型的全局變量是即占文件空間,又占用運(yùn)行時(shí)內(nèi)存空間的。
3、rodata?
rodata的意義同樣明顯,ro代表read only,即只讀數(shù)據(jù)(const)。關(guān)于rodata類型的數(shù)據(jù),要注意以下幾點(diǎn):?
l 常量不一定就放在rodata里,有的立即數(shù)直接編碼在指令里,存放在代碼段(.text)中。?
l 對(duì)于字符串常量,編譯器會(huì)自動(dòng)去掉重復(fù)的字符串,保證一個(gè)字符串在一個(gè)可執(zhí)行文件(EXE/SO)中只存在一份拷貝。?
l rodata是在多個(gè)進(jìn)程間是共享的,這可以提高空間利用率。?
l 在有的嵌入式系統(tǒng)中,rodata放在ROM(如norflash)里,運(yùn)行時(shí)直接讀取ROM內(nèi)存,無(wú)需要加載到RAM內(nèi)存中。?
l 在嵌入式linux系統(tǒng)中,通過一種叫作XIP(就地執(zhí)行)的技術(shù),也可以直接讀取,而無(wú)需要加載到RAM內(nèi)存中。
由此可見,把在運(yùn)行過程中不會(huì)改變的數(shù)據(jù)設(shè)為rodata類型的,是有很多好處的:在多個(gè)進(jìn)程間共享,可以大大提高空間利用率,甚至不占用RAM空間。同時(shí)由于rodata在只讀的內(nèi)存頁(yè)面(page)中,是受保護(hù)的,任何試圖對(duì)它的修改都會(huì)被及時(shí)發(fā)現(xiàn),這可以幫助提高程序的穩(wěn)定性。
4、變量與關(guān)鍵字?
static關(guān)鍵字用途太多,以致于讓新手模糊。不過,總結(jié)起來(lái)就有兩種作用,改變生命期和限制作用域。如:?
l 修飾inline函數(shù):限制作用域?
l 修飾普通函數(shù):限制作用域?
l 修飾局部變量:改變生命期?
l 修飾全局變量:限制作用域
const 關(guān)鍵字倒是比較明了,用const修飾的變量放在rodata里,字符串默認(rèn)就是常量。對(duì)const,注意以下幾點(diǎn)就行了。?
l 指針常量:指向的數(shù)據(jù)是常量。如 const char* p = “abc”; p指向的內(nèi)容是常量 ,但p本身不是常量,你可以讓p再指向”123”。?
l 常量指針:指針本身是常量。如:char* const p = “abc”; p本身就是常量,你不能讓p再指向”123”。?
l 指針常量 + 常量指針:指針和指針指向的數(shù)據(jù)都是常量。const char* const p =”abc”; 兩者都是常量,不能再修改。
violatile關(guān)鍵字通常用來(lái)修飾多線程共享的全局變量和IO內(nèi)存。告訴編譯器,不要把此類變量?jī)?yōu)化到寄存器中,每次都要老老實(shí)實(shí)的從內(nèi)存中讀取,因?yàn)樗鼈冸S時(shí)都可能變化。這個(gè)關(guān)鍵字可能比較生僻,但千萬(wàn)不要忘了它,否則一個(gè)錯(cuò)誤讓你調(diào)試好幾天也得不到一點(diǎn)線索。
總結(jié)
以上是生活随笔為你收集整理的data,bss和rodata段的区别与联系的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 教你拜访客户如何开场
- 下一篇: 法兰克机器人循环编程_FANUC机器人程