一. 內存模型 html
1. .text數組
代碼區(code section)。由編譯器連接器生成的可執行指令,程序執行時由加載器(loader)從可執行文件拷貝到內存中。爲了安全考慮,防止別的區域更改代碼區數據(便可執行指令),代碼區具備只讀屬性。另外一個方面,代碼區一般具備可共享性(sharable),即在內存中只有一份代碼區,如編譯器,假如同時有多個編譯任務在執行,這些編譯任務會共享編譯器的代碼區,但同時各個編譯任務又有本身獨立的區域。安全
2. .rodata函數
只讀數據區(read-only section)。包含:只讀全局變量,只讀字符串變量,只讀靜態(static)變量。程序執行時由加載器(loader)從可執行文件拷貝到內存中。工具
3. .dataspa
可寫數據區(RW section)。包括:可寫全局變量,可寫靜態(static)變量。程序執行時由加載器(loader)從可執行文件拷貝到內存中。.net
4. . bssdebug
未初始化數據區(un-initialized section)。包括:未初始化或初始化爲零的全局變量,未初始化或初始化爲零的靜態(static)變量。爲了減少可執行文件的大小,在可執行文件中bss區只是一個佔位符。在程序執行時,加載器(loader)根據bss區的大小,在內存中開闢相應空間,同時將這些內存空間所有初始化爲零。調試
.text, .rodata, .data, .bss四個區域,統稱爲編譯時內存(compiler-time memory),顧名思義,這些區域的大小在編譯時就能夠決定。code
5. heap
堆區。對於C語言而言,heap指程序運行時(run-time)由malloc, calloc, realloc等函數分配的內存。
6. stack
棧區。每一次函數調用,都會發生一次壓棧操做,被壓棧數據稱爲一個棧幀(stack frame),有多少次函數調用(包括main()函數),棧區就有多少個棧幀。相應的,每一次函數調用返回,都會相應的發生一次出棧操做,棧幀就會減小一個。
函數調用時,根據壓棧的順序,依次須要壓棧的數據包括:調用函數(caller funtion)的上下文環境(context environment),如寄存器;函數返回地址;被調用函數(called funtion)的參數列表;被調用函數的非靜態(static)局部變量。
當棧區溢出(stack overflow/underflow)時,棧區數據被污染,程序執行錯誤,甚至「跑飛"(函數返回地址被修改)。
7. 示例代碼
1 /* empty-main.c */ 2 #include <stdio.h> 3 4 int main(void) 5 { 6 return 0; 7 }
1 /* hello-mac.c */ 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 int g_init_2[2] = {1, 2}; /* .data */ 7 const int gc_int_3[3] = {1, 2, 3}; /* .rodata */ 8 int g_initWithZero_4[4] = {0}; /* .bss */ 9 int g_unInit_5[5]; /* .bss */ 10 11 extern int mac(int a, int b, int c); 12 13 int main(void) 14 { 15 static int s_init_6[6] = {1, 2, 3, 4, 5, 6}; /* .data */ 16 static const int sc_int_7[7] = {1, 2, 3, 4, 5 ,6, 7}; /* .rodata */ 17 static int s_initWithZero_8[8] = {0}; /* .bss */ 18 static int s_unInit_9[9]; /* .bss */ 19 20 int mac_out; /* stack */ 21 int *heap_10 = (int*)malloc(10 * sizeof(int)); /* heap */ 22 23 mac_out = mac(1, 2, 3); 24 printf("mac=%d\n", mac_out); /* .rodata string */ 25 26 free(heap_10); 27 return 0; 28 }
1 /* mac.c */ 2 3 #include <stdio.h> 4 5 int mac(int a, int b, int c) 6 { 7 return a + b * c;?? 8 }
二. 如何得到compiler-time memory consumption:.text, .rodata, .data, .bss
首先,必須說明的是,下面提到的三種方法,題主也有不少沒有弄明白的地方,尤爲是對於對齊的考慮。不過使用objdump -x的方法,能夠很清楚的驗證,上面示例代碼中對變量屬於哪一個區的描述都是正確的。
1. 藉助size/objdump等工具
首先,咱們使用gcc在Linux平臺編譯連接上面的hello-mac.c和mac.c兩個源文件:
$ gcc -o hello-mac hello-mac.c mac.c $ gcc -o empty-main empty-main.c
而後咱們可使用size或者objdump工具來比較兩個可執行文件的區別。
1.1 咱們嘗試用size命令看看:
$ size hello-mac empty-main text data bss dec hex filename 1540 616 184 2340 924 hello-mac 1115 552 8 1675 68b empty-main
其中text表示只讀區(.text和.rodata),data爲.data初始化的全局變量或靜態變量,bss表示未初始化全局變量或靜態變量。dec爲前三者的和,hex爲dec列的16進製表示。
這裏之因此使用empty-main,是爲了剔除掉glibc等系統佔用的內存。
1.2 咱們嘗試用objdump -x命令:
$ objdump -x hello-mac
$ objdump -x empty-main
在輸出中,咱們能查看到更加詳細的信息,比size的信息要多得多。包括咱們前面定義的全局變量和靜態變量分別屬於.rodata, .data和.bss,均有清晰的交待。
在輸出的map文件中,咱們能夠看到main和mac兩個函數的.text大小,並求他們的和。
在計算其餘區的時候,因爲該方法打印的結果能看到每一個函數,每一個變量屬於什麼區,佔多少內存,所以能夠精確的計算出size大小。
2. 藉助於連接器選項,生成map文件
對於GCC,添加-Xlinker -Map=<filename>到連接器選項便可;對於ARMCC,添加-L--map -L--list=<filename>到連接器選項便可;對於MSVS,按照Linker->debugging->Generate Map Files -> Yes修改就能夠獲得可執行文件的map問價。咱們再分析map文件就能夠了。下面用gcc作實驗。
$ gcc -c hello-mac.c -o hello-mac.o $ gcc -c mac.c -o mac.o $ gcc -o hello-mac -Xlinker -Map=hello-mac.map hello-mac.o mac.o
$ grep "\.text.*mac.o" ./hello-mac.map | awk '{print $3}' | awk '{sum+=$1} END {print "sum=", sum}' # get .text memory
經過查看map文件咱們能清晰的看出來哪一個源文件(在map中爲上面生成的.o目標文件)包含哪些函數,變量,各自佔了.text, .rodata, .data, .bss多少空間。這個方法對於全局變量,函數代碼區大小都能查到,可是,靜態變量並無被查到。
3. 對比
.text | .rodata | .data | .bss | |
size (hello-mac SUB empty-main) |
425 | 64 | 176 | |
objdump -x | 113 | 44 | 32 | 104 |
map file | 113 | 60 | 40 | 128 |
by hand | \ | 48 | 32 | 104 |
結論:size方法雖然減掉了empty-main,但明顯仍是引入了其餘的glibc裏的東西,致使計算偏大;objdump -x可以得出最詳細且準確的信息,可是該方法因爲沒有給出每一個變量和函數屬於哪一個源文件,所以對於大型軟件的統計不利;map file的方法則是前面兩種方法的折中,既能快速自動化的算出結果,結果又很是接近真實值。
三. 如何得到run-time memory consumption: heap, stack
在上面的內存模型中,咱們會發現heap和stack是向着相反的方向增加,那麼,若是二者相遇重疊了會發生什麼?要麼發生heap的數據被stack覆蓋,或者相反。
在調試程序時,經常會遇到「Segment Fault」, 「Stackoverflow", "Heap crash」, 最多見的緣由就是在於此。那麼
I. 是否能在程序運行時獲取程序當前的stack, heap大小,以及stack, heap的總容量呢?
II. 有時一個平臺上出現SegFault,可是在另外一個平臺就沒有了,如數組越界訪問,爲何?
這部分還不知道有什麼工具能看。TBD
四. 參考
http://blog.sina.com.cn/s/blog_af9acfc60101bbcy.html
http://blog.csdn.net/gl23838/article/details/7924254
http://www.geeksforgeeks.org/memory-layout-of-c-program/