1. 程式人生 > >64位系統下程序的記憶體佈局

64位系統下程序的記憶體佈局

環境

  1. 作業系統:ubuntu15.04
  2. 實體記憶體: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 命令

顯示系統啟動時的資訊