虛擬記憶體是電腦實體記憶體中劃分出來的一部分。虛擬記憶體是電腦系統記憶體管理的技術,它通常是被分隔成多個實體記憶體碎片,還有部分暫時儲存在外部磁碟記憶體上,在需要時進行資料交換。
作業系統有虛擬記憶體與實體記憶體的概念。在很久以前,還沒有虛擬記憶體概念的時候,程式尋址用的都是實體位址。程式能定址的範圍是有限的,這取決於CPU的位址線條數。例如在32位元平台下,尋址的範圍是2^32也就是4G。而這是固定的,如果沒有虛擬內存,且每次開啟一個進程都給4G的物理內存,就可能會出現很多問題:
因為我的物理內存時有限的,當有多個進程要執行的時候,都要給4G內存,很顯然你內存小一點,這很快就分配完了,於是沒有得到分配資源的進程就只能等待。當一個進程執行完了以後,再將等待的進程裝入記憶體。這種頻繁的裝入記憶體的操作是很沒效率的
由於指令都是直接存取物理記憶體的,那麼我這個進程就可以修改其他進程的數據,甚至會修改內核位址空間的數據,這是我們不想看到的
因為記憶體時隨機分配的,所以程式運行的位址也是不正確的。
於是針對上面會出現的各種問題,虛擬記憶體就出來了。
一個行程在執行時都會得到4G的虛擬記憶體。這個虛擬記憶體你可以認為,每個進程都認為自己擁有4G的空間,這只是每個進程認為的,但是實際上,在虛擬內存對應的物理內存上,可能只對應的一點點的物理內存,實際用了多少內存,就會對應多少物理內存。
進程得到的這4G虛擬記憶體是一個連續的位址空間(這也只是進程認為),而實際上,它通常是被分隔成多個物理記憶體碎片,還有一部分儲存在外部磁碟在記憶體上,在需要時進行資料交換。
程式開始要存取一個位址,它可能會經歷下面的過程
#每次我要存取位址空間上的某一個位址,都需要把位址翻譯為實際物理記憶體位址
所有行程共享這整一塊實體內存,每個行程只把自己目前需要的虛擬位址空間映射到物理記憶體上
#進程需要知道哪些位址空間上的資料在實體記憶體上,哪些不在(可能這部分儲存在磁碟上),還有在實體記憶體上的哪裡,這就需要透過頁表來記錄
頁表的每個表格項目分成兩部分,第一部分記錄此頁是否在實體記憶體上,第二部分記錄實體記憶體頁的位址(如果在的話)
當進程存取某個虛擬位址的時候,就會先去看頁表,如果發現對應的資料不在實體記憶體上,就會發生缺頁異常
再來總結虛擬記憶體是怎麼運作的#
當每個進程創建的時候,核心會為進程分配4G的虛擬內存,當進程還沒有開始運行時,這只是一個內存佈局。實際上並沒有立即就把虛擬記憶體對應位置的程式資料和程式碼(例如.text .data段)拷貝到實體記憶體中,只是建立好虛擬記憶體和磁碟檔案之間的映射就好(叫做記憶體對映)。這時候資料和程式碼還是在磁碟上的。當執行到對應的程式時,進程去尋找頁表,發現頁表中位址沒有存放在實體記憶體上,而是在磁碟上,於是發生缺頁異常,於是將磁碟上的資料拷貝到實體記憶體中。
另外在進程運行過程中,要透過malloc來動態分配記憶體時,也只是分配了虛擬內存,即為這塊虛擬內存對應的頁表項做相應設置,當進程真正訪問到此數據時,才引發缺頁異常。
可以認為虛擬空間都被映射到了磁碟空間中(事實上也是按需要映射到磁碟空間上,透過mmap,mmap是用來建立虛擬空間和磁碟空間的映射關係的)
利用虛擬記憶體機制的優點
既然每個行程的記憶體空間都是一致且固定的(32位元平台下方都是4G),所以連結器在連結可執行檔時,可以設定記憶體位址,而不用去管這些資料最終實際記憶體位址,這交給核心來完成映射關係
當不同的當行程使用同一段程式碼時,例如庫檔案的程式碼,在實體記憶體中可以只儲存一份這樣的程式碼,不同行程只要將自己的虛擬記憶體映射過去就好了,這樣可以節省實體記憶體
#在程式需要分配連續空間的時候,只需要在虛擬記憶體分配連續空間,而不需要實體記憶體時連續的,實際上,往往物理記憶體都是斷斷續續的記憶體碎片。這樣就可以有效地利用我們的實體記憶體
更多相關知識,請造訪:PHP中文網!
以上是虛擬記憶體是電腦記憶體的一部分嗎?的詳細內容。更多資訊請關注PHP中文網其他相關文章!