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