程式:只是一段程式碼,儲存在檔案中。
編譯器在編譯程式生成可執行檔案時,會對每一條指令和資料,進行地址排號。
程式執行時,就會將指令和資料放到指定的記憶體當中去。而程式只有在執行的時候才會佔據記憶體,因此程式地址空間又被叫做程序地址空間。
記憶體空間是這樣的。
若執行中的程式直接存取實體地址,會怎麼樣呢?
所以OS中設定了虛擬記憶體,通過虛擬地址空間對映到實體記憶體上。而使用C語言/C++時,變數或函數的地址,都是虛擬空間地址,實體記憶體地址使用者一概看不到,由OS統一管理。而OS負責將虛擬地址對映到對應的實體地址。
每執行一段程式,就會開闢連續的地址空間,若是每個程式佔據的空間比較大,很多程式共同執行,就會導致有的程式在記憶體中無法執行。而連續開闢的記憶體地址空間的空間使用率是很低的。
而程序使用了虛擬記憶體之後,每個程序都擁有自己的虛擬地址空間,都會有一塊連續的空間使用。
看一下這段程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int global_val = 200;
int main()
{
pid_t pid = fork();//建立子程序
if(pid < 0)
{
printf("fork error\n");
return 0;
}
else if(pid == 0)
{
printf("child:%d %p\n",global_val,&global_val);
}
else
{
printf("parent:%d %p\n",global_val,&global_val);
}
return 0;
}
其輸出為:
發現子程序中和父程序使用的是同樣的變數和地址。
對程式碼進行一點小更改:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int global_val = 200;
int main()
{
pid_t pid = fork();//建立子程序
if(pid < 0)
{
printf("fork error\n");
return 0;
}
else if(pid == 0)
{
global_val = 100;
printf("child:%d %p\n",global_val,&global_val);
}
else
{
sleep(3);
printf("parent:%d %p\n",global_val,&global_val);
}
return 0;
}
其輸出為:
可以看到子程序的變數改變了,而父程序的變數是沒有改變的。
為什麼子程序變數改變了,而父程序的變數沒有改變?
子程序是父程序的一份拷貝,子程序拷貝了父程序所有的資訊。在子程序中資料未發生改變的時候,子程序使用父程序的所有資訊。
在第一份程式碼中,子程序中變數沒有改變,父程序中變數沒有改變。所以第一份程式碼中,地址相等,變數也相等。
第二份程式碼中,子程序中變數發生了改變,父程序中變數沒有發生改變。相同的虛擬地址對映到了不同的實體地址。所以第二份程式碼,地址相同,變數不同。
這裡的相同是指:子程序拷貝了父程序所有的資訊,程序地址空間、PCB…
子程序資料發生更改,進行了拷貝了。
第二份程式碼中,這裡涉及到了寫時拷貝技術:Linux中fork()使用寫時拷貝實現。寫時拷貝是一種推遲或者免除拷貝的技術。OS並不複製整個程序地址空間,而是子程序父程序共用一個地址,當有資料寫入,發生改變時,資料才會被複制,使每個程序都有了自己的拷貝。資源的複製只有在寫入的時候才進行。 而在此之前,子程序只是可讀共用的,這樣就保證了父子程序的程式碼共用,資料獨立。
寫時拷貝技術帶來的好處:
那麼為什麼OS要使用虛擬地址空間?或者說虛擬地址空間帶了什麼好處?
虛擬地址是如何對映到實體地址的?
作業系統中記憶體管理方式:
段表:作業系統記錄記憶體分了多少塊。
通過段號尋找對應的實體記憶體起始地址,再加上段內偏移量,就找到了實體地址。
4* 1024* 1024* 1024/4* 1024
個頁號,即頁表項。2^20個
頁表項/頁號。將記憶體分為很多個細小的塊。通過找到對應的頁號,其實體地址和頁內偏移就可找到變數的實體地址。
當前計算機使用的段頁式管理。
虛擬頁會快取在實體記憶體中。如圖:
虛擬記憶體可快取到頁表中:頁命中,VP2就會快取在記憶體中。
快取不命中:缺頁 ,VP3不會命中,發生缺頁中斷。那麼OS就會從磁碟複製VP3到記憶體中PP3,再更新PTE3,隨後返回。
VP3:虛擬記憶體3.
PP3:實體記憶體3
PTE3:頁表條目3。0:發生中斷,1:可以快取。
經過缺頁中斷之後:缺頁處理程式會選擇一個作為犧牲頁,並從磁碟上VP3的副本取代它,
MMU利用頁表來實現虛擬地址空間到實體地址空空間的對映:
那麼該選擇犧牲頁呢?
採用記憶體置換演演算法: