高頻面試題——你真的搞懂物理內存與虛擬內存了嗎

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"},{"type":"underline"},{"type":"strong"}],"text":"公衆號【codeoffer】精選後端面試知識,歡迎關注"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"目錄"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"#%E4%B8%80%E3%80%81%C2%A0%E8%99%9A%E6%8B%9F%E5%9C%B0%E5%9D%80%E7%A9%BA%E9%97%B4","title":null},"content":[{"type":"text","text":"一、 虛擬地址空間"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"#%E4%BA%8C%E3%80%81%E5%88%86%E6%AE%B5%E4%B8%8E%E5%88%86%E9%A1%B5","title":null},"content":[{"type":"text","text":"二、分段與分頁"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"#%E4%B8%89%E3%80%81%E9%A1%B5%E8%A1%A8","title":null},"content":[{"type":"text","text":"三、頁表"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"#%E5%9B%9B%E3%80%81%E6%80%BB%E7%BB%93","title":null},"content":[{"type":"text","text":"四、總結"}]}]},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虛擬內存是什麼?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這是一個面試中經常被提到的問題,大多數人可能只記住了內存地址映射和缺頁置換,但你真的搞懂了爲什麼會有虛擬內存了嗎,它存在的作用是什麼呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要想搞懂虛擬內存,我們先從頭來回顧一下虛擬地址和分段分頁的知識吧。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"一、 虛擬地址空間"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在早期的計算機中,程序是直接運行在物理內存上的,那個時候的計算機和程序內存都很小。程序運行時會把其全部加載到內存,只要程序所需的內存不超過計算機剩餘內存就不會出現問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但由於程序是可以直接訪問物理內存的,這也帶來了內存數據的不安全性,輕則程序掛掉,重則操作系統崩潰。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,我們希望程序間的內存數據是安全的互不影響的。同時計算機程序直接運行在物理內存上也導致了內存使用率較低,程序運行內存地址不確定,不同的運行順序甚至會出錯。此時在程序的執行過程中,已經存在着大量在物理內存和硬盤之間的數據交換過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於以上問題,那我們可以是不是考慮在物理內存之上增加一箇中間層,讓程序通過虛擬地址去間接的訪問物理內存呢。通過虛擬內存,每個進程好像都可以獨佔內存一樣,每個進程看到的內存都是一致的,這稱爲虛擬地址空間。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(這種思想在現在也用的很廣泛,例如很多優秀的中間層:Nginx、Redis等等)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣只要系統處理好虛擬地址到物理地址的映射關係,就可以保證不同的程序訪問不同的內存區域,就可以達到物理內存地址隔離的效果,進而保證數據的安全性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"二、分段與分頁"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進程是操作系統資源分配的最小單元。操作系統分配給進程的內存空間中包含五種段:數據段、代碼段、BSS、堆、棧。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數據段:存放程序中的靜態變量和已初始化且不爲零的全局變量。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼段:存放可執行文件的操作指令,代碼段是隻讀的,不可進行寫操作。這部分的區域在運行前已知其大小。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"BSS段( Block Started By Symbol):存放未初始化的全局變量,在變量使用前由運行時初始化爲零。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"堆:存放進程運行中被動態分配的內存,其大小不固定。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"棧:存放程序中的臨時的局部變量和函數的參數值。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0d/0df091d25f30b7c46fda45d4739ad8b9.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼分段的技術可以解決什麼問題呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假設程序A的虛擬地址空間是0x00000000~0x00000099,映射到的物理地址空間是0x00000600~0x00000699,程序B的虛擬地址空間是0x00000100~0x00000199,映射到的物理地址空間是0x00000300~0x00000399。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假設你手殘,在程序A中操作了地址0x00000150,但是此時的地址0x00000150是虛擬的,而虛擬化的操作是在操作系統的掌控中的,所以,操作系統有能力判斷,這個虛擬地址0x00000150是有問題的,然後阻止後續的操作。所以,這裏體現出了隔離性。(另一種體現隔離性的方式就是,操作同一個虛擬地址,實際上可能操作的是不同的物理地址)"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以通過分段機制,我們可以更好的控制不同段的屬性,這有利於內存的組織安排,可以對不同的屬性代碼、數據進行更方便的管理。如果是打亂的放在內存中,那麼讀寫屬性就很難控制。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"程序運行地址和物理地址的隔離保證了程序內存數據的安全性,也解決了同一個程序運行地址不確定的問題,但是物理內存使用效率低下的問題依然沒有得到解決,因爲分段機制映射的是一片連續的物理內存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"於是大佬們又提出了分頁的辦法。分頁其實就是把段空間更細分了一下,粒度更小。此時物理內存被劃分爲一小塊一小塊,每塊被稱爲幀(Frame)。分配內存時,幀是分配時的最小單位。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在分段方法中,每次程序的運行都會被全部加載到虛擬內存中;而分頁方法則不同,單位不是整個程序,而是某個“頁”,一段虛擬地址空間組成的某一頁映射到一段物理地址空間組成的某一頁。它將少部分要運行的代碼加載到虛擬內存中,通過映射在物理內存中運行,從而提高了物理內存的使用率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e5/e542f644edefb86a8014fe7d17690496.jpeg","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了方便CPU高效執行管理物理內存,每一次都需要從虛擬內存中拿一個頁的代碼放到物理內存。虛擬內存頁有三種狀態,分別是未分配、已緩存和未緩存狀態。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"未分配:"},{"type":"text","text":"指的是未被操作系統分配或者創建的,未分配的虛擬頁不存在任何數據和代碼與它們關聯,因此不佔用磁盤資源;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"已緩存:"},{"type":"text","text":"表示的是物理內存中已經爲該部分分配的,存在虛擬內存和物理內存映射關係的;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"未緩存:"},{"type":"text","text":"指的是已經加載到虛擬內存中的,但是未在物理內存中建立映射關係的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"三、頁表"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虛擬內存中的一些虛擬頁是要緩存在物理內存中才能被執行的,因此操作系統存在一種機制用來判斷某個虛擬頁是否被緩存在物理內存中,還需要知道這個虛擬頁存放在磁盤上的哪個位置,從而在物理內存中選擇空閒頁或者更新緩存頁,並將需要的虛擬頁從磁盤複製到物理內存中。這些功能是由軟硬件結合完成的,其存放在物理內存中一個叫頁表的數據結構中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虛擬內存和物理內存的映射通過頁表(page table)來實現。每個頁表實際上是一個數組,數組中的每個元素稱爲頁表項(PTE, page table entry),每個頁表項負責把虛擬頁映射到物理頁上。在 物理內存中,每個進程都有自己的頁表。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/62/62f59325912379a00cea940552d8ba75.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲有一個表可以查詢,就會遇到兩種情況,一種是命中(Page Hit),另一種則是未命中(Page Fault)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"命中的時候,即訪問到頁表中藍色條目的地址時,因爲在 DRAM 中有對應的數據,可以直接訪問。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不命中的時候,即訪問到 page table 中灰色條目的時候,因爲在 DRAM 中並沒有對應的數據,所以需要執行缺頁置換。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上圖中,四個虛擬頁VP1 , VP2, VP4 , VP7 是被緩存在物理內存中。兩個虛擬頁VP0, VP5還未被分配。但是剩下的虛擬頁VP3 ,VP6已經被分配了,但是還沒有緩存到物理內存中去執行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"四、總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過虛擬地址空間和頁表的回顧,現在大家應該明白爲什麼要引入虛擬內存了吧。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虛擬內存是計算機系統內存管理的一種技術,虛擬地址空間構成虛擬內存。它使得應用程序認爲它擁有連續可用的內存(一個連續完整的地址空間),而實際上,它通常是被分隔成多個物理內存碎片。還有部分暫時存儲在外部磁盤存儲器上(Swap),在需要時進行數據交換。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"虛擬內存不只是用磁盤空間來擴展物理內存"},{"type":"text","text":" 的意思——這只是擴充內存級別以使其包含硬盤驅動器而已。把內存擴展到磁盤只是使用虛擬內存技術的一個結果。除了擴展內存空間,虛擬內存技術還有隔離運行內存和確定運行地址的作用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用虛擬內存主要是基於以下三個方面考慮,也就是說虛擬內存主要有三個作用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作爲緩存工具,提高內存利用率:使用 DRAM 當做部分的虛擬地址空間的緩存(虛擬內存就是存儲在磁盤上的 N 個連續字節的數組,數組的部分內容會緩存在 DRAM 中)。擴大了內存空間,當發生缺頁異常時,將會把內存和磁盤中的數據進行置換。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作爲內存管理工具,簡化內存管理:每個進程都有統一的線性地址空間(但實際上在物理內存中可能是間隔、支離破碎的),在內存分配中沒有太多限制,每個虛擬頁都可以被映射到任何的物理頁上。這樣也帶來一個好處,如果兩個進程間有共享的數據,那麼直接指向同一個物理頁即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作爲內存保護工具,隔離地址空間:進程之間不會相互影響;用戶程序不能訪問內核信息和代碼。頁表中的每個條目的高位部分是表示權限的位,MMU 可以通過檢查這些位來進行權限控制(讀、寫、執行)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,現在你應該搞懂虛擬內存是什麼了吧!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章