Linux下C程序进程地址空间布局
我們?cè)趯W(xué)習(xí)C程序開發(fā)時(shí)經(jīng)常會(huì)遇到一些概念:代碼段、數(shù)據(jù)段、BSS段(Block Started by Symbol)?、堆(heap)和棧(stack)。先看一張教材上的示意圖(來(lái)源,《UNIX環(huán)境高級(jí)編程》一書),顯示了進(jìn)程地址空間中典型的存儲(chǔ)區(qū)域分配情況。
???????????
從圖中可以看出:
- 從低地址到高地址分別為:代碼段、(初始化)數(shù)據(jù)段、(未初始化)數(shù)據(jù)段(BSS)、堆、棧、命令行參數(shù)和環(huán)境變量
- 堆向高內(nèi)存地址生長(zhǎng)
- 棧向低內(nèi)存地址生長(zhǎng)
還經(jīng)??吹较旅孢@個(gè)圖(來(lái)源,不詳):
???????????????????????????????????????????????????????????????????
?
先看一段程序。
?
view plain?
下面是輸出結(jié)果。
???????????
先仔細(xì)分析一下上面的輸出結(jié)果,看看能得出什么結(jié)論。貌似很難分析出來(lái)什么結(jié)果。好了我們繼續(xù)往下看吧。
?
接下來(lái),通過(guò)查看proc文件系統(tǒng)下的文件,看一下這個(gè)進(jìn)程的真實(shí)內(nèi)存分配情況。(我們需要在程序結(jié)束前加一個(gè)死循環(huán),不讓進(jìn)程結(jié)束,以便我們進(jìn)一步分析)。
??????在return 0前,增加 while(1); 語(yǔ)句
重新編譯后,運(yùn)行程序,程序?qū)⑦M(jìn)入死循環(huán)。
??????
使用ps命令查看一下進(jìn)程的pid
? #ps -aux | grep a.out
查看/proc/2699/maps文件,這個(gè)文件顯示了進(jìn)程在內(nèi)存空間中各個(gè)區(qū)域的分配情況。
? #cat? /proc/2699/maps
上面紅顏色標(biāo)出的幾個(gè)區(qū)間是我們感興趣的區(qū)間:
- 08048000-08049000? r-xp? 貌似是代碼段
- 08049000-0804a000 r--p?? 暫時(shí)不清楚,看不出來(lái)
- 0804a000-0804b000 rw-p? 貌似為數(shù)據(jù)段
- 08a7e000-08a9f000? rw-p? 堆
- bff73000-bff88000???? rw-p?? 棧???
我們把這些數(shù)據(jù)與剛才的程序運(yùn)行結(jié)果進(jìn)行比較,看看有什么結(jié)論。
??????????????? &global_init_a=0x804a018?????? 全局初始化:數(shù)據(jù)段??????????? ? global_init_a=1
????????????&global_uninit_a=0x804a04c??????全局未初始化:數(shù)據(jù)段?????? ? ?global_uninit_a=0
???? &static_global_init_a=0x804a01c ?? ??全局靜態(tài)初始化:數(shù)據(jù)段? ?? ?static_global_init_a=1
&static_global_uninit_a=0x804a038 ???? 全局靜態(tài)未初始化:數(shù)據(jù)段? ?? static_global_uninit_a=0
???????? ??? &const_global_a=0x80487c0 ?? ?全局只讀變量:?代碼段??????? const_global_a=1
???? ????????? ? &global_init_b=0x804a020???????全局初始化:數(shù)據(jù)段????? global_init_b=1
???? ??? ?? &global_uninit_b=0x804a048?????? ?全局未初始化:數(shù)據(jù)段????? global_uninit_b=0
??? ?&static_global_init_b=0x804a024????? ? 全局靜態(tài)初始化:數(shù)據(jù)段??? static_global_init_b=1
&static_global_uninit_b=0x804a03c??????? 全局靜態(tài)未初始化:數(shù)據(jù)段?? static_global_uninit_b=0
???????? ?? &const_global_b=0x80487c4????????全局只讀變量:?代碼段??????? ???? const_global_b=1
???????????? ??? &local_init_a=0xbff8600c ?? ????? 局部初始化:棧???????????????????? local_init_a=1
??????? ???? &local_uninit_a=0xbff86008 ?? ?????局部未初始化:棧???????????????? local_uninit_a=134514459
???? &static_local_init_a=0x804a028 ?? ???? 局部靜態(tài)初始化:數(shù)據(jù)段????? static_local_init_a=1
?&static_local_uninit_a=0x804a040??????? 局部靜態(tài)未初始化:數(shù)據(jù)段??? ?static_local_uninit_a=0
?????????? ? &const_local_a=0xbff86004????????局部只讀變量:棧???? const_local_a=1
???? ????????? ?? &local_init_b=0xbff86000????????局部初始化:棧???????? ?local_init_b=1
?? ???????????? &local_uninit_b=0xbff85ffc?????????局部未初始化:棧?????? ?local_uninit_b=-1074241512
???? ?&static_local_init_b=0x804a02c??????? 局部靜態(tài)初始化:數(shù)據(jù)段????? static_local_init_b=1
?&static_local_uninit_b=0x804a044??????? 局部靜態(tài)未初始化:數(shù)據(jù)段????? static_local_uninit_b=0
?????????? ???? &const_local_b=0xbff85ff8????????局部只讀變量:棧?????? ?const_local_b=1
?????????????????????????? p_chars=0x80487c8????????字符串常量:代碼段????????? p_chars=abcdef
????????????? ????? malloc_p_a=0x8a7e008 ?? ????malloc動(dòng)態(tài)分配:堆??????? *malloc_p_a=0
通過(guò)以上分析我們暫時(shí)可以得到的結(jié)論如下,在進(jìn)程的地址空間中:
- 數(shù)據(jù)段中存放:全局變量(初始化以及未初始化的)、靜態(tài)變量(全局的和局部的、初始化的以及未初始化的)
- 代碼段中存放:全局只讀變量(const)、字符串常量
- 堆中存放:動(dòng)態(tài)分配的區(qū)域
- 棧中存放:局部變量(初始化以及未初始化的,但不包含靜態(tài)變量)、局部只讀變量(const)
這里我們沒(méi)有發(fā)現(xiàn)BSS段,但是我們將未初始化的數(shù)據(jù)按照地址進(jìn)行排序看一下,可以發(fā)現(xiàn)一個(gè)規(guī)律。
??????????????? &global_init_a=0x804a018?????? 全局初始化:數(shù)據(jù)段??????????? ? global_init_a=1
??? &static_global_init_a=0x804a01c ?? ??全局靜態(tài)初始化:數(shù)據(jù)段? ?? ?static_global_init_a=1
??????????????? &global_init_b=0x804a020???????全局初始化:數(shù)據(jù)段????? global_init_b=1
????&static_global_init_b=0x804a024????? ? 全局靜態(tài)初始化:數(shù)據(jù)段??? static_global_init_b=1
???? ? &static_local_init_a=0x804a028 ?? ???? 局部靜態(tài)初始化:數(shù)據(jù)段????? static_local_init_a=1
? ?????&static_local_init_b=0x804a02c??????? 局部靜態(tài)初始化:數(shù)據(jù)段????? static_local_init_b=1
&static_global_uninit_a=0x804a038 ???? 全局靜態(tài)未初始化:數(shù)據(jù)段? ?? static_global_uninit_a=0
&static_global_uninit_b=0x804a03c??????? 全局靜態(tài)未初始化:數(shù)據(jù)段?? static_global_uninit_b=0
??&static_local_uninit_a=0x804a040??????? 局部靜態(tài)未初始化:數(shù)據(jù)段??? ?static_local_uninit_a=0
??&static_local_uninit_b=0x804a044??????? 局部靜態(tài)未初始化:數(shù)據(jù)段????? static_local_uninit_b=0
?????????? &global_uninit_b=0x804a048?????? ?全局未初始化:數(shù)據(jù)段????? global_uninit_b=0
????????????&global_uninit_a=0x804a04c??????全局未初始化:數(shù)據(jù)段?????? ? ?global_uninit_a=0
??? 這里可以發(fā)現(xiàn),初始化的和未初始化的數(shù)據(jù)好像是分開存放的,因此我們可以猜測(cè)BSS段是存在的,只不過(guò)數(shù)據(jù)段是分為初始化和未初始化(即BSS段)的兩部分,他們?cè)诩虞d到進(jìn)程地址空間時(shí)是合并為數(shù)據(jù)段了,在進(jìn)程地址空間中沒(méi)有單獨(dú)分為一個(gè)區(qū)域。
??? 還有一個(gè)問(wèn)題,靜態(tài)數(shù)據(jù)與非靜態(tài)數(shù)據(jù)是否是分開存放的呢?請(qǐng)讀者自行分析一下。
?
?接下來(lái)我們從程序的角度看一下,這寫存儲(chǔ)區(qū)域是如何分配的。首先我們先介紹一下ELF文件格式。
ELF(Executable and Linkable Format )文件格式是一個(gè)開放標(biāo)準(zhǔn),各種UNIX系統(tǒng)的可執(zhí)行文件都采用ELF格式,它有三種不同的類型: –可重定位的目標(biāo)文件(Relocatable,或者Object File) –可執(zhí)行文件(Executable) –共享庫(kù)(Shared Object,或者Shared Library) 下圖為ELF文件的結(jié)構(gòu)示意圖(來(lái)源,不詳):?????????????????????????????????????
?
一個(gè)程序編譯生成目標(biāo)代碼文件(ELF文件)的過(guò)程如下,此圖引自《程序員的自我修養(yǎng)》一書的一個(gè)圖:
?????????????????????????????????
可以通過(guò)readelf命令查看EFL文件的相關(guān)信息,例如 readelf? -a? a.out? ,我們只關(guān)心各個(gè)段的分配情況,因此我們使用以下命令:
# readelf -S?a.out ??????????????????????將這里的內(nèi)存布局與之前看到的程序的運(yùn)行結(jié)果進(jìn)行分析:
??????????????? &global_init_a=0x804a018?????? 全局初始化:數(shù)據(jù)段??????????? ? global_init_a=1
????????????&global_uninit_a=0x804a04c??????全局未初始化:BSS段?????? ? ?global_uninit_a=0
???? &static_global_init_a=0x804a01c ?? ??全局靜態(tài)初始化:數(shù)據(jù)段? ?? ?static_global_init_a=1
&static_global_uninit_a=0x804a038 ???? 全局靜態(tài)未初始化:BSS段? ?? static_global_uninit_a=0
???????? ??? &const_global_a=0x80487c0 ?? ?全局只讀變量:?只讀數(shù)據(jù)段??????? const_global_a=1
???? ????????? ? &global_init_b=0x804a020???????全局初始化:數(shù)據(jù)段????? global_init_b=1
???? ??? ?? &global_uninit_b=0x804a048?????? ?全局未初始化:BSS段????? global_uninit_b=0
??? ?&static_global_init_b=0x804a024????? ? 全局靜態(tài)初始化:數(shù)據(jù)段??? static_global_init_b=1
&static_global_uninit_b=0x804a03c??????? 全局靜態(tài)未初始化:BSS段?? static_global_uninit_b=0
???????? ?? &const_global_b=0x80487c4????????全局只讀變量:?只讀數(shù)據(jù)段???????????? const_global_b=1
???? &static_local_init_a=0x804a028 ?? ???? 局部靜態(tài)初始化:數(shù)據(jù)段????? static_local_init_a=1
?&static_local_uninit_a=0x804a040??????? 局部靜態(tài)未初始化:BSS段??? ?static_local_uninit_a=0
??????&static_local_init_b=0x804a02c??????? 局部靜態(tài)初始化:數(shù)據(jù)段????? static_local_init_b=1
?&static_local_uninit_b=0x804a044??????? 局部靜態(tài)未初始化:BSS段????? static_local_uninit_b=0
?????????????????????????? p_chars=0x80487c8????????字符串常量:只讀數(shù)據(jù)段????????? p_chars=abcdef
ELF 文件一般包含以下幾個(gè)段 :
- .text section:主要是編譯后的源碼指令,是只讀字段。
- .data section :初始化后的非const的全局變量、局部static變量。
- .bss:未初始化后的非const全局變量、局部static變量。
- .rodata字段? 是存放只讀數(shù)據(jù)?
分析到這以后,我們?cè)诤椭胺治龅慕Y(jié)果對(duì)比一下,會(huì)發(fā)現(xiàn)確實(shí)存在BSS段,地址為0804a030?,大小為0x20,之前我們的程序中未初始化的的確存放在這個(gè)地址區(qū)間中了,只不過(guò)執(zhí)行exec系統(tǒng)調(diào)用時(shí),將這部分的數(shù)據(jù)初始化為0后,放到了進(jìn)程地址空間的數(shù)據(jù)段中了,在進(jìn)程地址空間中就沒(méi)有必要存在BSS段了,因此都稱做數(shù)據(jù)段。同理,.rodata字段也是與text段放在一起了。
在ELF文件中,找不到局部非靜態(tài)變量和動(dòng)態(tài)分配的內(nèi)容。
?
以上有很多地方的分析,我在網(wǎng)上基本找不到很明確的結(jié)論,很多教材上也沒(méi)有描述,只是我通過(guò)程序分析得出的結(jié)論,如有不妥之處,請(qǐng)指出,歡迎交流。
總結(jié)
以上是生活随笔為你收集整理的Linux下C程序进程地址空间布局的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: SQL SELECT DISTINCT
- 下一篇: Linux系统编程8:入门篇之简单明了说