在早期的計算機(jī)中蒋荚,是沒有虛擬內(nèi)存的概念的。我們要運行一個程序腐缤,會把程序全部裝入內(nèi)存喷橙,然后運行。
會出現(xiàn)以下問題:
1)進(jìn)程地址空間不隔離,沒有權(quán)限保護(hù)。
直接訪問物理內(nèi)存,可修改其他進(jìn)程的內(nèi)存數(shù)據(jù)泛豪。
2)內(nèi)存使用效率低。
大量數(shù)據(jù)的裝入裝出侦鹏。
3)程序運行的地址不確定诡曙。
內(nèi)存地址隨機(jī)分。
虛擬內(nèi)存的作用:
1)給所有進(jìn)程提供一致的地址空間略水,每個進(jìn)程都認(rèn)為自己是在獨占使用單機(jī)系統(tǒng)的存儲資源价卤。可以控制物理內(nèi)存的權(quán)限聚请。
2)內(nèi)存保護(hù):保護(hù)每個進(jìn)程的地址空間不被其他進(jìn)程破壞荠雕,隔離了進(jìn)程的地址訪問。
3)根據(jù)緩存原理驶赏,上層存儲是下層存儲的緩存炸卑,虛擬內(nèi)存把主存作為磁盤的高速緩存,在主存和磁盤之間根據(jù)需要來回傳送數(shù)據(jù)煤傍,高效地使用了主存盖文。
4)VA到PA的映射會給分配和釋放內(nèi)存帶來方便。物理內(nèi)存不連續(xù)的地址蚯姆,可映射到連續(xù)的虛擬內(nèi)存地址五续。
虛擬內(nèi)存機(jī)制的優(yōu)點
- 擴(kuò)大地址空間;
- 內(nèi)存保護(hù):每個進(jìn)程運行在各自的虛擬內(nèi)存地址空間龄恋,互相不能干擾對方疙驾。
- 每個進(jìn)程的內(nèi)存空間都是一致而且固定的(32位平臺下都是4G),所以鏈接器在鏈接可執(zhí)行文件時郭毕,可以設(shè)定內(nèi)存地址它碎,而不用去管這些數(shù)據(jù)最終實際內(nèi)存地址,這交給內(nèi)核來完成映射關(guān)系
- 當(dāng)不同的進(jìn)程使用同一段代碼時显押,比如庫文件的代碼扳肛,在物理內(nèi)存中可以只存儲一份這樣的代碼,不同進(jìn)程只要將自己的虛擬內(nèi)存映射過去就好了乘碑,這樣可以節(jié)省物理內(nèi)存
- 在程序需要分配連續(xù)空間的時候挖息,只需要在虛擬內(nèi)存分配連續(xù)空間,而不需要物理內(nèi)存時連續(xù)的兽肤,實際上套腹,往往物理內(nèi)存都是斷斷續(xù)續(xù)的內(nèi)存碎片绪抛。這樣就可以有效地利用我們的物理內(nèi)存
- 虛擬內(nèi)存很適合在多道程序設(shè)計系統(tǒng)中使用,許多程序的片段同時保存在內(nèi)存中电禀。當(dāng)一個程序等待它的一部分讀入內(nèi)存時睦疫,可以把CPU交給另一個進(jìn)程使用。在內(nèi)存中可以保留多個進(jìn)程鞭呕,系統(tǒng)并發(fā)度提高。
虛擬內(nèi)存的代價:
1.虛存的管理需要建立很多數(shù)據(jù)結(jié)構(gòu)宛官,這些數(shù)據(jù)結(jié)構(gòu)要占用額外的內(nèi)存
2.虛擬地址到物理地址的轉(zhuǎn)換葫松,增加了指令的執(zhí)行時間。
3.頁面的換入換出需要磁盤I/O底洗,這是很耗時的
4.如果一頁中只有一部分?jǐn)?shù)據(jù)腋么,會浪費內(nèi)存。
虛擬地址:
對于32位系統(tǒng)亥揖,尋址指針為4字節(jié)珊擂,對應(yīng)的虛擬地址空間為0-2^32,即0-4G费变。
對于64位系統(tǒng)摧扇,尋址指針為8字節(jié),對應(yīng)的虛擬地址空間為0-2^64挚歧,即0-16G扛稽。不是實際存在的。
Linux內(nèi)核把虛擬地址空間分為兩部分:用戶進(jìn)程空間滑负,內(nèi)核進(jìn)程空間在张。
在緩存原理中,換入/換出的數(shù)據(jù)以塊為最小單位矮慕。在內(nèi)存管理時帮匾,頁是地址空間的最小單位。
虛擬地址到物理地址的轉(zhuǎn)換
進(jìn)程得到的這4G虛擬內(nèi)存是一個連續(xù)的地址空間(這也只是進(jìn)程認(rèn)為)痴鳄,而實際上瘟斜,它通常是被分隔成多個物理內(nèi)存碎片,還有一部分存儲在外部磁盤存儲器上夏跷,在需要時進(jìn)行數(shù)據(jù)交換哼转。
進(jìn)程開始要訪問一個地址,經(jīng)歷下面的過程
- 訪問地址空間上的某一個地址槽华,都需要把地址翻譯為實際物理內(nèi)存地址壹蔓;
- 所有進(jìn)程共享這整一塊物理內(nèi)存,每個進(jìn)程只把自己目前需要的虛擬地址空間映射到物理內(nèi)存上猫态;
- 進(jìn)程需要知道哪些地址空間上的數(shù)據(jù)在物理內(nèi)存上佣蓉,哪些不在(可能這部分存儲在磁盤上)披摄,還有在物理內(nèi)存上的哪里,這就需要通過頁表來記錄勇凭;
- 頁表的每一個表項分兩部分疚膊,第一部分記錄此頁是否在物理內(nèi)存上,第二部分記錄物理內(nèi)存頁的地址(如果在的話)虾标;
- 當(dāng)進(jìn)程訪問某個虛擬地址的時候寓盗,就會先去看頁表,如果發(fā)現(xiàn)對應(yīng)的數(shù)據(jù)不在物理內(nèi)存上璧函,就會發(fā)生缺頁異常傀蚌;
- 缺頁異常的處理過程,操作系統(tǒng)立即阻塞該進(jìn)程蘸吓,并將硬盤里對應(yīng)的頁換入內(nèi)存善炫,然后使該進(jìn)程就緒,如果內(nèi)存已經(jīng)滿了库继,沒有空地方了箩艺,那就找一個頁覆蓋,至于具體覆蓋的哪個頁宪萄,就需要看操作系統(tǒng)的頁面置換算法是怎么設(shè)計的了艺谆。
虛擬內(nèi)存是怎么工作的
當(dāng)每個進(jìn)程創(chuàng)建的時候,內(nèi)核會為進(jìn)程分配4G的虛擬內(nèi)存拜英,當(dāng)進(jìn)程還沒有開始運行時擂涛,這只是一個內(nèi)存布局。實際上并不立即就把虛擬內(nèi)存對應(yīng)位置的程序數(shù)據(jù)和代碼(比如.text .data段)拷貝到物理內(nèi)存中聊记,只是建立好虛擬內(nèi)存和磁盤文件之間的映射就好(叫做存儲器映射)撒妈。這個時候數(shù)據(jù)和代碼還是在磁盤上的。當(dāng)運行到對應(yīng)的程序時排监,進(jìn)程去尋找頁表狰右,發(fā)現(xiàn)頁表中地址沒有存放在物理內(nèi)存上,而是在磁盤上舆床,于是發(fā)生缺頁異常棋蚌,于是將磁盤上的數(shù)據(jù)拷貝到物理內(nèi)存中。
另外在進(jìn)程運行過程中挨队,要通過malloc來動態(tài)分配內(nèi)存時谷暮,也只是分配了虛擬內(nèi)存,即為這塊虛擬內(nèi)存對應(yīng)的頁表項做相應(yīng)設(shè)置盛垦,當(dāng)進(jìn)程真正訪問到此數(shù)據(jù)時湿弦,才引發(fā)缺頁異常。
可以認(rèn)為虛擬空間都被映射到了磁盤空間中(事實上也是按需要映射到磁盤空間上腾夯,通過mmap颊埃,mmap是用來建立虛擬空間和磁盤空間的映射關(guān)系的)