研究背景:kernel 2.6.32 32位
程序地址空間
先看一段代碼:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int g_val = 0;
int main(){
pid_t id = fork();
if(id < 0){
perror("fork");
return 0;
}
else if(id == 0){
g_val = 100;
printf("child[%d]: %d : %p \n",getpid(),g_val,&g_val);
}
else{
sleep(3);
printf("parent[%d]: %d : %p \n",getpid(),g_val,&g_val);
}
return 0 ; }
明明子進程時先於父進程運行的,並且對 g_val 的值進行了修改,爲什麼父進程運行的時候 g_val 的值反而還是 0?並且子進程和父進程中的 g_val 變量所存在的地址都一樣的?
得出結論:
變量內容不一樣,所以父子進程輸出的變量絕對不是同一個變量
地址一樣,說明該地址絕對不是物理地址
在Linux環境下,這個地址叫做 虛擬地址,我們在用 C/C++ 看到的地址,全部都是虛擬地址! 物理地址,用戶一概看不到,由 OS 統一管理;
進程地址空間
上面的這張圖就很好的解釋了上面的那段代碼:同一個變量,地址相同,起始就是虛擬地址相同,內容不同其實是被映射到了不同的物理地址。
操作系統通過 mm_struct 這個結構體個進程描述了一個虛擬的地址空間;
mm_struct{
ulong size;
ulong code_size;
ulong code_end;
ulong data_start;
ulong data_end;
};
虛擬地址空間又通過 頁表 對於到相應的物理內存上,但是實際上在物理內存的管理是通過 頁段式內存的管理方式 管理的;
虛擬地址空間 + 頁表:提高了內存的利用率、對內存訪問進行控制;
虛擬地址空間 + 頁表 優點:保持了進程的獨立性、充分利用內存、內存訪問控制
寫時拷貝技術
父進程創建子進程創建了子進程,但是並沒有直接給子進程開闢空間,拷貝數據。而是跟父進程映射到同一塊內存空間,但是 如果數據發生了變化,那麼就需要重新給子進程開闢內存,並且更新頁表信息,提高子進程的創建性能。