虛幻與真實:程式中的地址如何轉換?
1)讀取指令、讀寫資料的時候需要和記憶體進行怎樣的互動?
-
告訴記憶體晶片:hi,記憶體老哥請你把 0x10000 地址處的資料交給我……hi,記憶體老哥,我已經計算完成,請讓我把結果寫回 0x200000 地址的空間。這些地址存在於程式碼指令欄位後的常數,或者存在於某個暫存器中。
設想一下,如果一臺計算機的記憶體中只執行一個程式 A,這種方式正好用前面 CPU 的真實模式來執行,因為程式 A 的地址在連結時就可以確定,例如從記憶體地址 0x8000 開始,每次執行程式 A 都裝入記憶體 0x8000 地址處開始執行,沒有其它程式干擾。現在改變一下,記憶體中又放一道程式 B,程式 A 和程式 B 各自執行一秒鐘,如此迴圈,直到其中之一結束。這個新場景下就會產生一些問題,當然這裡我們只關心記憶體相關的這幾個核心問題。
2) 誰來保證程式 A 跟程式 B 沒有記憶體地址的衝突?
-
換句話說,就是程式 A、B 各自放在什麼記憶體地址,這個問題是由 A、B 程式協商,還是由作業系統決定?
3)如何解決記憶體容量問題?
-
程式 A 和程式 B,在不斷開發迭代中程式程式碼佔用的空間會越來越大,導致記憶體裝不下?
4)如果還要拓展其他程式又該怎麼辦?
-
如果不只程式 A、B,還可能有程式 C、D、E、F、G……它們分別由不同的公司開發,而每臺計算機的記憶體容量不同。這時候,又對我們的記憶體方案有怎樣的影響呢?
5)怎樣完美的解決以上最核心的 4 個問題?
-
所有的程式都各自享有一個從 0 開始到最大地址的空間,這個地址空間是獨立的,是該程式私有的,其它程式既看不到,也不能訪問該地址空間,這個地址空間和其它程式無關,和具體的計算機也無關。---虛擬地址
6)什麼是虛擬地址?
-
不存在的地址,邏輯上的,和具體的環境無關。這個環境包括軟體環境和硬體環境。
7)我們用 objdump 工具反彙編一下 Hello World 二進位制檔案看看長什麼樣?
00000000000004e8 <_init>:
4e8: 48 83 ec 08 sub $0x8,%rsp
4ec: 48 8b 05 f5 0a 20 00 mov 0x200af5(%rip),%rax # 200fe8 <__gmon_start__>
4f3: 48 85 c0 test %rax,%rax
4f6: 74 02 je 4fa <_init+0x12>
4f8: ff d0 callq *%rax
4fa: 48 83 c4 08 add $0x8,%rsp
4fe: c3 retq
-
左邊第一列資料就是虛擬地址,第三列中是程式指令,如:“mov 0x200af5(%rip),%rax,je 4fa,callq *%rax”指令中的資料都是虛擬地址。
8)為什麼所有的應用程式開始的部分都是這樣的?
-
因為每個應用程式的虛擬地址空間都是相同且獨立的。
9)那麼這個地址是由誰產生的呢?
-
連結器
10)什麼是實體地址呢?
-
一個會被地址譯碼器等電子部件變成電子訊號的資料,這個電子訊號放到地址總線上去,多個訊號組合起來呢就可以表示為我們記憶體的一個儲存單元了。
11)地址總線上的訊號(即實體地址)除了可以選擇記憶體之外,還可以選擇那些地方?
-
顯示卡中的視訊記憶體、I/O 裝置中的暫存器、網絡卡上的網路幀快取器。
12)虛擬地址到實體地址如何轉換?
-
轉換機構:相當於一個函式:p=f(v),輸入虛擬地址 v,輸出實體地址 p。
13)那麼要怎麼實現這個函式呢?
-
軟體方式實現太低效,用硬體實現沒有靈活性。
-
最終就用了軟硬體結合的方式實現,它就是 MMU(記憶體管理單元)。
14)MMU 工作原理框架圖是怎樣的?
-
注意地址關係轉換表本身則是放實體記憶體中的。
15)地址關係轉換表中邏輯地址和實體地址的對應關係是怎樣的?
-
把虛擬地址空間和實體地址空間都分成同等大小的塊,也稱為頁,按照虛擬頁和物理頁進行轉換。根據軟體配置不同,這個頁的大小可以設定為 4KB、2MB、4MB、1GB,這樣就進入了現代記憶體管理模式——分頁模型。
16)分頁模型框架是怎樣的?
-
一個虛擬頁可以對應到一個物理頁,頁大小固定。所以在地址關係轉換表中,只要存放虛擬頁地址對應的物理頁地址就行了。
17)MMU是做什麼工作的?
-
接受虛擬地址和地址關係轉換表,以及輸出實體地址。
18)MMU的工作需要什麼前提條件?
-
必須先開啟保護模式或者長模式,真實模式下是不能開啟 MMU 的。
19)保護模式的記憶體模型是分段模型,而MMU是分頁模型的,那怎麼說保護模式下可以工作?
-
別忘了我們的保護模式下還有個平坦模式,繞過我們的分段模型。
20)地址產生的過程是怎樣的?
21)地址關係轉換表更專業的名字是什麼?
-
頁表
22)為了提高查詢效率,頁表是分級的,那麼頁表分級的結構是怎樣的?
-
一個頂級頁目錄,多箇中級頁目錄,最後才是頁表。
23)開啟 MMU,步驟是怎樣的?
-
使 CPU 進入保護模式或者長模式。
-
準備好頁表資料,這包含頂級頁目錄,中間層頁目錄,頁表,假定我們已經編寫了程式碼,在實體記憶體中生成了這些資料。
-
把頂級頁目錄的實體記憶體地址賦值給 CR3 暫存器。
mov eax, PAGE_TLB_BADR ;頁表實體地址
mov cr3, eax
-
設定 CPU 的 CR0 的 PE 位為 1,這樣就開啟了 MMU。
;開啟 保護模式和分頁模式
mov eax, cr0
bts eax, 0 ;CR0.PE =1
bts eax, 31 ;CR0.P = 1
mov cr0, eax
24)MMU 的主要功能是根據頁表資料把虛擬地址轉換成實體地址,但有沒有可能轉換失敗?
-
絕對有可能,例如,頁表項中的資料為空,使用者程式訪問了超級管理者的頁面,向只讀頁面中寫入資料。這些都會導致 MMU 地址轉換失敗。
25)MMU 地址轉換失敗了怎麼辦呢?
-
MMU 停止轉換地址。
-
MMU 把轉換失敗的虛擬地址寫入 CPU 的 CR2 暫存器。
-
MMU 觸發 CPU 的 14 號中斷,使 CPU 停止執行當前指令。
-
CPU 開始執行 14 號中斷的處理程式碼,程式碼會檢查原因,處理好頁表資料返回。
-
CPU 中斷返回繼續執行 MMU 地址轉換失敗時的指令。
26)在分頁模式下,作業系統是如何對應用程式的地址空間進行隔離的?
-
對於每個程序而言,它會誤認為(被作業系統欺騙)自己獨有所有地址空間,因此它訪問地址是不會考慮任何問題的,可是這個地址是虛擬地址,待被MMU翻譯後會得到對應的頁表,而這個頁表由作業系統管理,不同的程序擁有不同的頁表,也因此產生了程序地址空間隔離,但是多個程序也是可以共享某個頁表,這也是程序通訊(IPC)的根本手段。