=========================================================================
個人主頁點擊直達:小白不是程序媛
Linux專欄:Linux系統化學習
代碼倉庫:Gitee
=========================================================================
目錄
虛擬地址和物理地址
頁表
進程地址空間
進程地址空間存在的意義
虛擬地址和物理地址
我們在學習C/C++的時候肯定都見過下面這張有關于內存分布的圖片:
在來段代碼理解感受下:
1 #include<stdio.h>2 #include<stdlib.h>3 //未初始化常量4 int un_gval;5 //初始化常量6 int init_gval=100;7 int main()8 {9 //代碼區地址10 printf("code addr: %p\n",main);11 //字符常量12 const char *str="hellolinux!";13 14 //常量區地址15 printf("read only char addr : %p\n",str);16 //已初始化全局數據區17 printf("init global value addr: %p\n",&init_gval);18 //未初始化全局數據區19 printf("uninit global value addr: %p\n",&un_gval);20 21 char *heap1=(char*)malloc(100);22 char *heap2=(char*)malloc(100);23 char *heap3=(char*)malloc(100);24 char *heap4=(char*)malloc(100);25 static int a=0;26 printf("heap1 addr:%p\n",heap1); 27 printf("heap2 addr:%p\n",heap2);28 printf("heap3 addr:%p\n",heap3);29 printf("heap4 addr:%p\n",heap4);30 31 printf("stack addr:%p\n",&str);32 printf("stack addr:%p\n",&heap1);33 printf("stack addr:%p\n",&heap2);34 printf("stack addr:%p\n",&heap3);35 printf("stack addr:%p\n",&heap4);36 printf("a addr:%p\n",&a);37 38 return 0;39 }
通過上面這段代碼,我們好像不僅驗證了上面的空間分布圖片,而且還發現了棧區和堆區相向而生的內存開辟特點。
上兩篇文章我們介紹了命令行參數和環境變量,其實這兩個就儲存在棧區之上的空間,來段代碼驗證下:
1 #include<stdio.h>
W> 2 int main(int argc , char *argv[], char *env[])3 {4 int i=0;5 printf("i addr:%p\n",&i); 6 for(;argv[i];i++)7 {8 printf("argv[%d]:%p\n",i,argv[i]);9 }10 for(i=0;env[i];i++)11 {12 printf("env[%d]:%p\n",i,env[i]);13 }14 return 0;15 }~
?
驗證完這些,話說回來其實我們之前學的對內存的概念就上面所介紹的內容其實都不是真正意義上的內存是虛擬內存,不是我們真正意義上的內存物理地址。
1 #include<stdio.h>2 #include<unistd.h>3 #include<stdlib.h> 4 int g_val = 100;5 6 int main()7 {8 pid_t id = fork();9 if(id == 0)10 {11 //child12 int cnt = 5;13 while(1)14 {15 printf("child, Pid: %d, Ppid: %d, g_val: %d, &g_val=%p\n", getpid(), getppid(), g_val, &g_val);16 sleep(1);17 if(cnt == 0)18 {19 g_val=200;20 printf("child change g_val: 100->200\n");21 }22 cnt--;23 }24 }25 else26 {27 //father28 while(1)29 {30 printf("father, Pid: %d, Ppid: %d, g_val: %d, &g_val=%p\n", getpid(), getppid(), g_val, &g_val);31 sleep(1);32 }33 }34 35 sleep(100);36 return 0;37 }
我們發現,父子進程,輸出地址是一致的,但是變量內容不一樣!能得出如下結論:
- 變量內容不一樣,所以父子進程輸出的變量絕對不是同一個變量?
- 但地址值是一樣的,說明,該地址絕對不是物理地址!
- 在Linux地址下,這種地址叫做 虛擬地址
- 我們在用C/C++語言所看到的地址,全部都是虛擬地址!物理地址,用戶一概看不到,由OS統一管理OS必須負責將 虛擬地址 轉化成 物理地址?
我們再將同一個可執行程序同時運行也會發現兩個進程的獲取到的地址竟然也是一樣的。
?話又說回來,我們的可執行程序運行時肯定會加載到內存中,因此虛擬地址和物理地址一定有關聯,這個關聯就是頁表。
頁表
頁表就是將虛擬地址和物理地址聯系起來的一種模型,其中還包括變量是否可以被修改,進程的狀態等諸多信息。
上面的圖就足矣說名問題,同一個變量,地址相同,其實是虛擬地址相同,內容不同其實是被映射到了不同的物理地址!
每個進程的頁表的物理地址存在與CPU中CR3的寄存器中
進程地址空間
進程地址空間其實我們可以使用內存大小的一個范圍,以我們32位總線的機器為例:它的范圍為0000 0000 -> ffff ffff ,也就是0到2^32次方(我們所謂的4GB)。模擬其物理空間大小進行區域劃分后形成棧區、堆區等等的虛擬地址,操作系統通過結構體將每個區域的起始和結束統計記錄起來,進程的PCB中含有指向這個結構體的指針。
因此,每當新的進程創建時會形成對應的PCB,PCB和PCB中的虛擬地址結構體指針和頁表關聯起來,對真正上的物理地址進行使用。?
進程地址空間存在的意義
- 讓進程以統一的視角看待內存,所以任意一個進程,可以通過地址空間和頁表可以將亂序的內存數據,變成有序,分門別類的規劃好
- 存在虛擬地址空間,可以有效的進行進程訪問內存的安全檢查
- 將進程管理和內存管理進行解耦,通過頁表讓進程映射到不同的物理內存處,從而實現進程的獨立性。
今天對Linux下進程地址空間的介紹分享到這就結束了,希望大家讀完后有很大的收獲,也可以在評論區點評文章中的內容和分享自己的看法。您三連的支持就是我前進的動力,感謝大家的支持!!!