elf中的bss data
生活随笔
收集整理的這篇文章主要介紹了
elf中的bss data
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
一般C語言的編譯后執(zhí)行語句都編譯成機器代碼,保存在.text段;已初始化的全局變量和局部靜態(tài)變量都保存在. data段;未初始化的全局變量和局部靜態(tài)變量一般放在一個叫.“bss”的段里。我們知道未初始化的全局變量和局部靜態(tài)變量默認值都為0,本來它們也可以被放在.data段的,但是因為它們都是0,所以為它們在.data段分配空間并且存放數(shù)據(jù)0是沒有必要的。局部數(shù)據(jù)在程序執(zhí)行過程的棧中進行初始化。運行的時候它們的確是要占內(nèi)存空間的,并且可執(zhí)行文件必須記錄所有未初始化的全局變量和局部靜態(tài)變量的大小總和,記為.bss段。所以.bss段只是為未初始化的全局變量和局部靜態(tài)變量預留位置而已,它并沒有內(nèi)容,所以它在文件中也不占據(jù)空間。數(shù)據(jù)段包含經(jīng)過初始化的全局變量以及它們的值。然后鏈接器得到這個大小的內(nèi)存塊,緊跟在數(shù)據(jù)段后面。當這個內(nèi)存區(qū)進入程序的地址空間后全部清零。包含數(shù)據(jù)段和段的整個區(qū)段此時通常稱為數(shù)據(jù)區(qū)。在采用段式內(nèi)存管理的架構中(比如intel的80x86系統(tǒng)),bss段(Block Started by Symbol segment)通常是指用來存放程序中未初始化的全局變量的一塊內(nèi)存區(qū)域,一般在初始化時bss 段部分將會清零。bss段屬于靜態(tài)內(nèi)存分配,即程序一開始就將其清零了。
比如,在C語言之類的程序編譯完成之后,已初始化的全局變量保存在.data 段中,未初始化的全局變量保存在.bss 段中。在《Programming ground up》里對.bss的解釋為:There is another section called the .bss. This section is like the data section, except that it doesn’t take up space in the executable.text和data段都在可執(zhí)行文件中(在嵌入式系統(tǒng)里一般是固化在鏡像文件中),由系統(tǒng)從可執(zhí)行文件中加載;而bss段不在可執(zhí)行文件中,由系統(tǒng)初始化。
有人可能會說,全局內(nèi)存就是全局變量嘛,有必要專門一章來介紹嗎?這么簡單的東西,還能玩出花來?我從來沒有深究它,不一樣寫程序嗎?關于全局內(nèi)存這個主題雖然玩不出花來,但確實有些重要,了解這些知識,對于優(yōu)化程序的時間和空間很有幫助。因為有好幾次這樣經(jīng)歷,我才決定花一章篇幅來介紹它。
正如大家所知道的,全局變量是放在全局內(nèi)存中的,但反過來卻未必成立。用static修飾的局部變量就是放在放全局內(nèi)存的,它的作用域是局部的,但生命期是全局的。在有的嵌入式平臺中,堆實際上就是一個全局變量,它占用相當大的一塊內(nèi)存,在運行時,把這塊內(nèi)存進行二次分配。
這里我們并不強調(diào)全局變量和全局內(nèi)存的差別。在本文中,全局強調(diào)的是它的生命期,而不是它的作用域,所以有時可能把兩者的概念互換。
一般來說,在一起定義的兩個全局變量,在內(nèi)存的中位置是相鄰的。這是一個簡單的常識,但有時挺有用,如果一個全局變量被破壞了,不防先查查其前后相關變量的訪問代碼,看看是否存在越界訪問的可能。
在ELF格式的可執(zhí)行文件中,全局內(nèi)存包括三種:bss、data和rodata。其它可執(zhí)行文件格式與之類似。了解了這三種數(shù)據(jù)的特點,我們才能充分發(fā)揮它們的長處,達到速度與空間的最優(yōu)化。
1.? ?? ?? ?bss
已經(jīng)記不清bss代表Block Storage Start還是Block Started by Symbol。像這我這種沒有用過那些史前計算機的人,終究無法明白這樣怪異的名字,也就記不住了。不過沒有關系,重要的是,我們要清楚bss全局變量有什么樣特點,以及如何利用它。
通俗的說,bss是指那些沒有初始化的和初始化為0的全局變量。它有什么特點呢,讓我們來看看一個小程序的表現(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類型的全局變量只占運行時的內(nèi)存空間,而不占文件空間。
另外,大多數(shù)操作系統(tǒng),在加載程序時,會把所有的bss全局變量全部清零,無需要你手工去清零。但為保證程序的可移植性,手工把這些變量初始化為0也是一個好習慣。
2.? ?? ?? ?data
與bss相比,data就容易明白多了,它的名字就暗示著里面存放著數(shù)據(jù)。當然,如果數(shù)據(jù)全是零,為了優(yōu)化考慮,編譯器把它當作bss處理。通俗的說,data指那些初始化過(非零)的非const的全局變量。它有什么特點呢,我們還是來看看一個小程序的表現(xiàn)。
int data_array[1024 * 1024] = {1};
int main(int argc, char* argv[])
{
? ? return 0;
}
[root@localhost data]# gcc -g data.c -o data.exe
[root@localhost 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
僅僅是把初始化的值改為非零了,文件就變?yōu)?M多。由此可見,data類型的全局變量是即占文件空間,又占用運行時內(nèi)存空間的。
3.? ?? ?? ?rodata
rodata的意義同樣明顯,ro代表read only,即只讀數(shù)據(jù)(const)。關于rodata類型的數(shù)據(jù),要注意以下幾點:
l? ?? ?? ?常量不一定就放在rodata里,有的立即數(shù)直接編碼在指令里,存放在代碼段(.text)中。
l? ?? ?? ?對于字符串常量,編譯器會自動去掉重復的字符串,保證一個字符串在一個可執(zhí)行文件(EXE/SO)中只存在一份拷貝。
l? ?? ?? ?rodata是在多個進程間是共享的,這可以提高空間利用率。
l? ?? ?? ?在有的嵌入式系統(tǒng)中,rodata放在ROM(如norflash)里,運行時直接讀取ROM內(nèi)存,無需要加載到RAM內(nèi)存中。
l? ?? ?? ?在嵌入式linux系統(tǒng)中,通過一種叫作XIP(就地執(zhí)行)的技術,也可以直接讀取,而無需要加載到RAM內(nèi)存中。
由此可見,把在運行過程中不會改變的數(shù)據(jù)設為rodata類型的,是有很多好處的:在多個進程間共享,可以大大提高空間利用率,甚至不占用RAM空間。同時由于rodata在只讀的內(nèi)存頁面(page)中,是受保護的,任何試圖對它的修改都會被及時發(fā)現(xiàn),這可以幫助提高程序的穩(wěn)定性。
4.? ?? ?? ?變量與關鍵字
static關鍵字用途太多,以致于讓新手模糊。不過,總結起來就有兩種作用,改變生命期和限制作用域。如:
l? ?? ?? ?修飾inline函數(shù):限制作用域
l? ?? ?? ?修飾普通函數(shù):限制作用域
l? ?? ?? ?修飾局部變量:改變生命期
l? ?? ?? ?修飾全局變量:限制作用域
const 關鍵字倒是比較明了,用const修飾的變量放在rodata里,字符串默認就是常量。對const,注意以下幾點就行了。
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關鍵字通常用來修飾多線程共享的全局變量和IO內(nèi)存。告訴編譯器,不要把此類變量優(yōu)化到寄存器中,每次都要老老實實的從內(nèi)存中讀取,因為它們隨時都可能變化。這個關鍵字可能比較生僻,但千萬不要忘了它,否則一個錯誤讓你調(diào)試好幾天也得不到一點線索。
比如,在C語言之類的程序編譯完成之后,已初始化的全局變量保存在.data 段中,未初始化的全局變量保存在.bss 段中。在《Programming ground up》里對.bss的解釋為:There is another section called the .bss. This section is like the data section, except that it doesn’t take up space in the executable.text和data段都在可執(zhí)行文件中(在嵌入式系統(tǒng)里一般是固化在鏡像文件中),由系統(tǒng)從可執(zhí)行文件中加載;而bss段不在可執(zhí)行文件中,由系統(tǒng)初始化。
有人可能會說,全局內(nèi)存就是全局變量嘛,有必要專門一章來介紹嗎?這么簡單的東西,還能玩出花來?我從來沒有深究它,不一樣寫程序嗎?關于全局內(nèi)存這個主題雖然玩不出花來,但確實有些重要,了解這些知識,對于優(yōu)化程序的時間和空間很有幫助。因為有好幾次這樣經(jīng)歷,我才決定花一章篇幅來介紹它。
正如大家所知道的,全局變量是放在全局內(nèi)存中的,但反過來卻未必成立。用static修飾的局部變量就是放在放全局內(nèi)存的,它的作用域是局部的,但生命期是全局的。在有的嵌入式平臺中,堆實際上就是一個全局變量,它占用相當大的一塊內(nèi)存,在運行時,把這塊內(nèi)存進行二次分配。
這里我們并不強調(diào)全局變量和全局內(nèi)存的差別。在本文中,全局強調(diào)的是它的生命期,而不是它的作用域,所以有時可能把兩者的概念互換。
一般來說,在一起定義的兩個全局變量,在內(nèi)存的中位置是相鄰的。這是一個簡單的常識,但有時挺有用,如果一個全局變量被破壞了,不防先查查其前后相關變量的訪問代碼,看看是否存在越界訪問的可能。
在ELF格式的可執(zhí)行文件中,全局內(nèi)存包括三種:bss、data和rodata。其它可執(zhí)行文件格式與之類似。了解了這三種數(shù)據(jù)的特點,我們才能充分發(fā)揮它們的長處,達到速度與空間的最優(yōu)化。
1.? ?? ?? ?bss
已經(jīng)記不清bss代表Block Storage Start還是Block Started by Symbol。像這我這種沒有用過那些史前計算機的人,終究無法明白這樣怪異的名字,也就記不住了。不過沒有關系,重要的是,我們要清楚bss全局變量有什么樣特點,以及如何利用它。
通俗的說,bss是指那些沒有初始化的和初始化為0的全局變量。它有什么特點呢,讓我們來看看一個小程序的表現(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類型的全局變量只占運行時的內(nèi)存空間,而不占文件空間。
另外,大多數(shù)操作系統(tǒng),在加載程序時,會把所有的bss全局變量全部清零,無需要你手工去清零。但為保證程序的可移植性,手工把這些變量初始化為0也是一個好習慣。
2.? ?? ?? ?data
與bss相比,data就容易明白多了,它的名字就暗示著里面存放著數(shù)據(jù)。當然,如果數(shù)據(jù)全是零,為了優(yōu)化考慮,編譯器把它當作bss處理。通俗的說,data指那些初始化過(非零)的非const的全局變量。它有什么特點呢,我們還是來看看一個小程序的表現(xiàn)。
int data_array[1024 * 1024] = {1};
int main(int argc, char* argv[])
{
? ? return 0;
}
[root@localhost data]# gcc -g data.c -o data.exe
[root@localhost 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
僅僅是把初始化的值改為非零了,文件就變?yōu)?M多。由此可見,data類型的全局變量是即占文件空間,又占用運行時內(nèi)存空間的。
3.? ?? ?? ?rodata
rodata的意義同樣明顯,ro代表read only,即只讀數(shù)據(jù)(const)。關于rodata類型的數(shù)據(jù),要注意以下幾點:
l? ?? ?? ?常量不一定就放在rodata里,有的立即數(shù)直接編碼在指令里,存放在代碼段(.text)中。
l? ?? ?? ?對于字符串常量,編譯器會自動去掉重復的字符串,保證一個字符串在一個可執(zhí)行文件(EXE/SO)中只存在一份拷貝。
l? ?? ?? ?rodata是在多個進程間是共享的,這可以提高空間利用率。
l? ?? ?? ?在有的嵌入式系統(tǒng)中,rodata放在ROM(如norflash)里,運行時直接讀取ROM內(nèi)存,無需要加載到RAM內(nèi)存中。
l? ?? ?? ?在嵌入式linux系統(tǒng)中,通過一種叫作XIP(就地執(zhí)行)的技術,也可以直接讀取,而無需要加載到RAM內(nèi)存中。
由此可見,把在運行過程中不會改變的數(shù)據(jù)設為rodata類型的,是有很多好處的:在多個進程間共享,可以大大提高空間利用率,甚至不占用RAM空間。同時由于rodata在只讀的內(nèi)存頁面(page)中,是受保護的,任何試圖對它的修改都會被及時發(fā)現(xiàn),這可以幫助提高程序的穩(wěn)定性。
4.? ?? ?? ?變量與關鍵字
static關鍵字用途太多,以致于讓新手模糊。不過,總結起來就有兩種作用,改變生命期和限制作用域。如:
l? ?? ?? ?修飾inline函數(shù):限制作用域
l? ?? ?? ?修飾普通函數(shù):限制作用域
l? ?? ?? ?修飾局部變量:改變生命期
l? ?? ?? ?修飾全局變量:限制作用域
const 關鍵字倒是比較明了,用const修飾的變量放在rodata里,字符串默認就是常量。對const,注意以下幾點就行了。
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關鍵字通常用來修飾多線程共享的全局變量和IO內(nèi)存。告訴編譯器,不要把此類變量優(yōu)化到寄存器中,每次都要老老實實的從內(nèi)存中讀取,因為它們隨時都可能變化。這個關鍵字可能比較生僻,但千萬不要忘了它,否則一個錯誤讓你調(diào)試好幾天也得不到一點線索。
總結
以上是生活随笔為你收集整理的elf中的bss data的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安川机器人程序还原_工业机器人还原存档程
- 下一篇: Ngrok编译