轉自:https://zhuanlan.zhihu.com/p/354142930
MMU原理(原圖)
MMU(Memory Management Unit),即內存管理單元,是現代CPU架構中不可或缺的一部分,MMU主要包含以下幾個功能:
- 虛實地址翻譯
在用戶訪問內存時,將用戶訪問的虛擬地址翻譯為實際的物理地址,以便CPU對實際的物理地址進行訪問。
- 訪問權限控制
可以對一些虛擬地址進行訪問權限控制,以便於對用戶程序的訪問權限和范圍進行管理,如代碼段一般設置為只讀,如果有用戶程序對代碼段進行寫操作,系統會觸發異常。
- 引申的物理內存管理
對系統的物理內存資源進行管理,為用戶程序提供物理內存的申請、釋放等操作接口。
使用MMU帶來的好處或者優勢:
- 提升物理內存的利用率
物理內存按需申請,如代碼段的內存在執行時進行映射和轉換,進程fork后,t通過寫時復制(Copy-On-Write)進行真正的物理內存分配。解決內存管理碎片化的問題,即在系統運行一段時間后,頻繁的內存申請和釋放會導致內存碎片化,無法申請到一塊足夠大的地址連續的內存。
- 對內存地址的訪問進行控制
如上述代碼段只讀權限控制,多線程的棧內存之間的空洞頁隔離可以防止棧溢出后改寫其他線程的棧內存,不同進程之間的地址隔離等等。
- 將進程的地址空間隔離
不同進程之間可以使用相同的虛擬內存地址空間,而進程間的物理內存又可以做到隔離,這保證了進程的獨立性同時,又簡化了地址的訪問方式,如在早期32位CPU上,為了支持4G以上的物理內存,一般物理地址有36-bit(如PowerPC-604系列),但是用戶的虛地址仍然使用32-bit,做法就是將用戶的不同進程的32-bit虛地址在MMU轉換時,轉換為36-bit的物理地址,這樣每個進程仍然能訪問0-3G虛地址范圍,將多個進程的3G空間映射到36-bit的物理內存空間中去。
MMU的由來
- Swap模式時代
早期計算機在執行程序時,將程序從磁盤加載到內存執行中執行,在多用戶系統中,當新的用戶程序被執行前,需要先將當前用戶的程序從內存swap到磁盤,然后從磁盤加載新的程序執行,當前用戶退出后,在將前一用戶程序從磁盤中加載到內存繼續執行,每次用戶切換伴隨程序的swap,消耗較大。
圖1: Swap示意
- Page模式時代
后來人們將內存划分為固定大小的Page,一般為4K或者更小,這樣用戶程序按需以Page的方式加載到內存,不需要將整個程序加載到內存,這樣內存可同時容納更多程序,而無需按照用戶切換進行swap,提升了內存利用率和加載時間(PageFault是Page時代的產物,而非MMU時代獨有)。
圖2: Page模式
- 動態地址轉換(DAT - Dynamic Address Translation)
最早可以追溯到1966年IBM研發的System/360-Model67,在該計算機的設計中首先引入了動態的地址轉換機制,在Page模式基礎上,為用戶程序分配虛地址(VA),通過DAT轉換為物理地址(PA)進行訪問。通過好處是用戶可以使用連續的地址,而不再受制於物理內存大小和Page碎片化的限制,原則上用戶的程序只受磁盤大小限制,代價是增加虛實地址轉換機制。
實現方式
采用segment、page、offset模式,將24-bit虛擬地址分為3段,0-7bit保留,8-11bit索引segment,一共16個segment,12-19bit索引page,每個segment最多256個page,20-31bit為page offset,每個用戶有一個虛實地址映射表,分為2級,即segment表和page表,每個segment指向一個page表。
轉換過程
當CPU訪問一個虛地址時,先通過VA的8-11bit查找segment表得到page表,再根據12-19bit在page表中找到PA的page起始地址,加上20-31bit的page offset就得到實際的PA了。
這就是MMU最早的雛形了。
圖3: 動態地址轉換
現代MMU的實現
現代CPU的MMU的地址轉換方式一般分為3種模式,即實地址模式、塊地址轉換、頁地址轉換。
圖4: 現代MMU
- 實地址模式
即CPU狀態位中MMU使能位清零,MMU處於關閉狀態,此時CPU操作的地址不經過轉換(VA=PA),直接作為物理地址進行訪問,CPU上電時或者在異常入口時處於該狀態,在該狀態下可以訪問任意物理內存,非常危險,一般操作系統在CPU上電后做完必要初始化以后便使能MMU,或者在異常處理的入口保存好必要信息后使能MMU。
- 塊地址轉換
或者成為固定的地址轉換或靜態配置的地址轉換表,這種模式支持配置一些固定的內存地址映射(VPN->RPN),比如Linux Kernel加載的地址,以PowerPC604為例,0xC0000000這段地址開始的256M內存映射使用了該模式的轉換,好處是這種配置轉換速度快,一般在特定的寄存器中配置,沒有頁表查找過程,缺點是缺乏靈活性,一次配置永久使用。
- 頁地址轉換
類似於早期的動態地址轉換DAT,即將VA的一部分bit用於索引segment,另外一部分bit用於索引PTE表,最終得到物理地址的Page起始地址,再加上最后12bit(4K)的Page offset得到真正的物理地址。
相比早期的DAT,有以下優化:
增加了PTE表的緩存TLB(Translation lookaside Buffer),有些處理器將ITLB(指令)和DTLB(數據)分開,以減少指令和數據之間的緩存沖突。
- 支持更大的物理地址(36bit以上)或邏輯地址,如在PowerPC中,用以應對現代操作系統的多進程管理將32bit通過segment寄存器擴展為52bit的邏輯地址,然后通過hash函數得到key用來查找PTE,並最終轉換為36bit物理地址,邏輯地址的擴展用於減少多進程之間的PTE沖突。
- 支持多種PTE查找方式,如硬件查找和軟件查找。
現在CPU的MMU地址轉換流程如下:
圖5: 現代MMU處理流程
主要分為幾個階段:
- 用戶進程訪問虛存地址。
- 觸發TLB查找過程,該部分通過硬件完成(灰色背景),沒有軟件參與。
- TLB miss場景下,查找PTE(粉色背景),該部分在不同CPU上實現不同,像X86都是硬件查找,PowerPC有些處理器使用軟件查找,即在內核實現一個TLB miss的異常處理,可以靈活做到TLB查找。
- Do Page Fault,分為幾種情況:
新申請內存第一次讀寫,觸發物理內存分配;進程fork后子進程寫內存觸發Copy-On-Write;非法內存讀寫,錯誤處理。
總結
本文簡單的將MMU的功能和演進過程做了一個框架性的介紹,以及現代MMU的簡易處理過程,其中詳細的機制后續有機會再做詳細介紹,比如多進程的TLB緩存處理,PTE的多級頁表查找,NUMA,MMU虛擬化等,同時隨着用戶的需求場景不斷變化,MMU的功能也會不斷更新升級,比如虛擬機的出現,催熟了MMU虛擬化功能,未來異構的硬件和系統中,比如CPU和GPU配合,GPU虛擬化等等,MMU將如何演進?歡迎大家一起探討。
參考資料:
[1] https://en.wikipedia.org/wiki/Memory_management_unit
[2] https://en.wikipedia.org/wiki/Memory_paging
凱哥stack
著作權歸作者所有,禁止轉載