64位系統下程序的記憶體佈局
環境
- 作業系統:ubuntu15.04
- 實體記憶體:4G
測試程式
#include<stdio.h>
#include<stdlib.h>
int a;
int b=1;
main()
{
int n = 0;
char *p1 = NULL;
char *p2 = NULL;
const int s = 10;
p1 = (char*)malloc(200);
p2 = "hello";
printf("main %p\n",main);
printf("未初始化 a %p\n" ,&a);
printf("初始化 b %p\n",&b);
printf("區域性變數 n %p\n",&n);
printf("動態記憶體 p1 %p\n",p1);
printf("常量 s %p\n",&s);
printf("常字串 p2 %p\n",p2);
while(1)
{}
}
檢視記憶體地址空間方式
以下2種方式可以結合著觀察
1、 pmap 命令
pmap [引數] [程序pid]
引數:
- -d 顯示詳細裝置資訊
- -q 不顯示首尾行資訊
執行測試程式,用top或ps 命令查詢程序pid:
屬性 | 含義 |
---|---|
Address | 地址空間:起始地址~ |
Kbytes | 大小 |
Mode | 許可權:r可讀、w可寫、x可執行、s共享記憶體、p私有記憶體 |
Offset | 虛擬記憶體偏移量 |
Device | 所在裝置(主:次): 008:00008表示sda8 |
mapped | 虛擬記憶體分配大小 |
shared | 共享記憶體大小 |
mem 是執行程式的名字
.so 是使用的動態連結庫
stack 使用的棧空間
anon 預分配的虛擬記憶體,還未有資料佔用
2、檢視 /proc/pid號/maps 檔案內容
這個要比上面顯示的更詳細一點
每一行依次對應的是:
地址範圍、許可權、偏移量、裝置、檔案inode、對映物件
第1行 [可讀可執行] 是程式的文字段
第2行 [只讀]………….不清楚
第3行 [可讀可寫] 是程式的資料段(包括初始化資料段和未初始化資料段)
第4行 ………………..堆空間
第16行 ………………棧空間
用 ls -i 可檢視原始檔的inode,符合第5列;連結庫在根分割槽下,我的是sda8,源程式在home下,我的是sda8,符合第4列。
3、程式的執行結果
從程式的入口地址main 0x400586 可以看出,maps檔案的第一行確實是程式的程式碼段。
全域性變數(初始化和未初始化)位於資料段,對應第3行
區域性變數位於棧段
const 常量位於棧段
常字串位於文字段
動態申請記憶體位於堆段
64位系統中應該有48根地址匯流排,低位:0~47位才是有效的可變地址,高位:48~63位全補0或全補1。一般高位全補0對應的地址空間是使用者態,如上面的第1~18行。高位全補1對應的是核心態,如上面的第19行。這64位的地址空間並不能全部被使用(太多了),所以使用者態和核心態之間會有未使用的空間(據說叫AMD64空洞)。
由上圖的檔案內容可以看出,從低地址到高地址記憶體佈局依次是:
保留區 –> 文字段–>資料段—>堆—>共享庫—>棧–>環境變數—>核心態
由pmap 命令和maps的顯示可看出,棧和堆的中間部分是共享庫,棧空間地址減小向下增長,堆空間地址增大向上增長。我覺得堆疊增長的時候,資料會先往緊挨的已分配記憶體anon中放,anon不夠用時再繼續增長,新分配記憶體。
dmesg 命令
顯示系統啟動時的資訊