名稱簡介
MMU是Memory Management Unit的縮寫,中文名是存儲器管理單元,它是中央處理器(CPU)中用來管理
器、物理存儲器的控制線路,同時也負責虛擬地址映射為物理地址,以及提供硬體機制的記憶體訪問授權。
MMU的歷史
許多年以前,當人們還在使用DOS或是更古老的作業系統的時候,計算機的記憶體還非常小,一般都是以K為單位進行計
算,相應的,當時的程式規模也不大,所以記憶體容量雖然小,但還是可以容納當時的程式。但隨著圖形界面的興起還用用戶需求的不斷增大,應用程式的規模也隨之膨脹起來,終於一個難題出現在程式設計師的面前,那就是應用程式太大以至於記憶體容納不下該程式,通常解決的辦法是把程式分割成許多稱為復蓋塊(overlay)的片段。復蓋塊0首先運行,結束時他將調用另一個復蓋塊。雖然復蓋塊的交換是由OS完成的,但是必須先由程式設計師把程式先進行分割,這是一個費時費力的工作,而且相當枯燥。人們必須找到更好的辦法從根本上解決這個問題。不久人們找到了一個辦法,這就是虛擬存儲器(virtual memory).虛擬存儲器的基本思想是程式,數據,堆疊的總的大小可以超過物理存儲器的大小,作業系統把當前使用的部分保留在記憶體中,而把其他未被使用的部分保存在磁碟上。比如對一個16MB的程式和一個記憶體只有4MB的機器,作業系統通過選擇,可以決定各個時刻將哪4M的內容保留在記憶體中,並在需要時在記憶體和磁碟間交換程式片段,這樣就可以把這個16M的程式運行在一個只具有4M記憶體機器上了。而這個16M的程式在運行前不必由程式設計師進行分割。
概念分析
——地址範圍、虛擬地址映射為物理地址 以及 分頁機制
任何時候,計算機上都存在一個程式能夠產生的地址集合,我們稱之為地址範圍。這個範圍的大小由CPU的位數決定,例如一個32位的CPU,它的地址範圍是0~0xFFFFFFFF (4G),而對於一個64位的CPU,它的地址範圍為0~0xFFFFFFFFFFFFFFFF (16E).這個範圍就是我們的程式能夠產生的地址範圍,我們把這個地址範圍稱為虛擬地址空間,該空間中的某一個地址我們稱之為虛擬地址。與虛擬地址空間和虛擬地址相對應的則是物理地址空間和物理地址,大多數時候我們的系統所具備的物理地址空間只是虛擬地址空間的一個子集。這裡舉一個最簡單的例子直觀地說明這兩者,對於一台記憶體為256M的32bit x86主機來說,它的虛擬地址空間範圍是0~0xFFFFFFFF(4G),而物理地址空間範圍是0x000000000~0x0FFFFFFF(256M)。
在沒有使用虛擬存儲器的機器上,虛擬地址被直接送到記憶體匯流排上,使具有相同地址的物理存儲器被讀寫;而在使用了虛擬存儲器的情況下,虛擬地址不是被直接送到記憶體地址匯流排上,而是送到存儲器管理單元MMU,把虛擬地址映射為物理地址。
大多數使用虛擬存儲器的系統都使用一種稱為分頁(paging)機制。虛擬地址空間劃分成稱為頁(page)的單位,而相應的物理地址空間也被進行劃分,單位是頁幀(frame).頁和頁幀的大小必須相同。在這個例子中我們有一台可以生成32位地址的機器,它的虛擬地址範圍從0~0xFFFFFFFF(4G),而這台機器只有256M的物理地址,因此他可以運行4G的程式,但該程式不能一次性調入記憶體運行。這台機器必須有一個達到可以存放4G程式的外部存儲器(例如磁碟或是FLASH),以保證程式片段在需要時可以被調用。在這個例子中,頁的大小為4K,頁幀大小與頁相同——這點是必須保證的,因為記憶體和外圍存儲器之間的傳輸總是以頁為單位的。對應4G的虛擬地址和256M的物理存儲器,他們分別包含了1M個頁和64K個頁幀。
主要功能
將線性地址映射為物理地址
現代的多用戶多進程作業系統,需要MMU,才能達到每個用戶進程都擁有自己獨立的地址空間的目標。使用MMU,作業系統劃分出一段地址區域,在這塊地址區域中,每個進程看到的內容都不一定一樣。例如MICROSOFT WINDOWS作業系統將地址範圍4M-2G劃分為用戶地址空間,進程A在地址0X400000(4M)映射了執行檔,進程B同樣在地址0X400000(4M)映射了執行檔,如果A進程讀地址0X400000,讀到的是A的執行檔映射到RAM的內容,而進程B讀取地址0X400000時,則讀到的是B的執行檔映射到RAM的內容。
這就是MMU在當中進行地址轉換所起的作用。
提供硬體機制的記憶體訪問授權
多年以來,微處理器一直帶有片上存儲器管理單元(MMU),MMU能使單個軟體執行緒工作於硬體保護地址空間。但是在許多商用實時作業系統中,即使系統中含有這些硬體也沒採用MMU。
當應用程式的所有執行緒共享同一存儲器空間時,任何一個執行緒將有意或無意地破壞其它執行緒的代碼、數據或堆疊。異常執行緒甚至可能破壞核心代碼或內部數據結構。例如執行緒中的指針錯誤就能輕易使整個系統崩潰,或至少導致系統工作異常。
就安全性和可靠性而言,基於進程的實時作業系統(RTOS)的性能更為優越。為生成具有單獨地址空間的進程,RTOS只需要生成一些基於RAM的數據結構並使MMU加強對這些數據結構的保護。基本思路是在每個關聯轉換中“接入”一組新的邏輯地址。MMU利用當前映射,將在指令調用或數據讀寫過程中使用的邏輯地址映射為存儲器物理地址。MMU還標記對非法邏輯地址進行的訪問,這些非法邏輯地址並沒有映射到任何物理地址。
這些進程雖然增加了利用查詢表訪問存儲器所固有的系統開銷,但其實現的效益很高。在進程邊界處,疏忽或錯誤操作將不會出現,用戶接口執行緒中的缺陷並不會導致其它更關鍵執行緒的代碼或數據遭到破壞。目前在可靠性和安全性要求很高的複雜嵌入式系統中,仍然存在采無存儲器保護的作業系統的情況,這實在有些不可思議。
採用MMU還有利於選擇性地將頁面映射或解映射到邏輯地址空間。物理存儲器頁面映射至邏輯空間,以保持當前進程的代碼,其餘頁面則用於數據映射。類似地,物理存儲器頁面通過映射可保持進程的執行緒堆疊。RTOS可以在每個執行緒堆疊解映射之後,很容易地保留邏輯地址所對應的頁面內容。這樣,如果任何執行緒分配的堆疊發生溢出,將產生硬體存儲器保護故障,核心將掛起該執行緒,而不使其破壞位於該地址空間中的其它重要存儲器區,如另一執行緒堆疊。這不僅線上程之間,還在同一地址空間之間增加了存儲器保護。
存儲器保護(包括這類堆疊溢出檢測)在應用程式開發中通常非常有效。採用了存儲器保護,程式錯誤將產生異常並能被立即檢測,它由原始碼進行跟蹤。如果沒有存儲器保護,程式錯誤將導致一些細微的難以跟蹤的故障。實際上,由於在扁平存儲器模型中,RAM通常位於物理地址的零頁面,因此甚至NULL指針引用的解除都無法檢測到。
相互關係
X86系列的MMU
INTEL出品的80386CPU或者更新的CPU中都集成有MMU. 可以提供32BIT共4G的地址空間.
X86 MMU提供的定址模式有4K/2M/4M的PAGE模式(根據不同的CPU,提供不同的能力),此處提供的是目前大部分作業系統使用的4K分頁機制的描述,並且不提供ACCESS CHECK的部分。
涉及的暫存器
a) GDT
b) LDT
c) CR0
d) CR3
e) SEGMENT REGISTER
虛擬地址到物理地址的轉換步驟
a) SEGMENT REGISTER作為GDT或者LDT的INDEX,取出對應的GDT/LDT ENTRY.
注意: SEGMENT是無法取消的,即使是FLAT模式下也是如此. 說FLAT模式下不使用SEGMENT REGISTER是錯誤的. 任意的RAM定址指令中均有DEFAULT的SEGMENT假定. 除非使用SEGMENT OVERRIDE PREFⅨ來改變當前定址指令的SEGMENT,否則使用的就是DEFAULT SEGMENT.
ENTRY格式
typedef struct
{
UINT16 limit_0_15;
UINT16 base_0_15;
UINT8 base_16_23;
UINT8 accessed : 1;
UINT8 readable : 1;
UINT8 conforming : 1;
UINT8 code_data : 1;
UINT8 app_system : 1;
UINT8 dpl : 2;
UINT8 present : 1;
UINT8 limit_16_19 : 4;
UINT8 unused : 1;
UINT8 always_0 : 1;
UINT8 seg_16_32 : 1;
UINT8 granularity : 1;
UINT8 base_24_31;
} CODE_SEG_DESCRIPTOR,*PCODE_SEG_DESCRIPTOR;
typedef struct
{
UINT16 limit_0_15;
UINT16 base_0_15;
UINT8 base_16_23;
UINT8 accessed : 1;
UINT8 writeable : 1;
UINT8 expanddown : 1;
UINT8 code_data : 1;
UINT8 app_system : 1;
UINT8 dpl : 2;
UINT8 present : 1;
UINT8 limit_16_19 : 4;
UINT8 unused : 1;
UINT8 always_0 : 1;
UINT8 seg_16_32 : 1;
UINT8 granularity : 1;
UINT8 base_24_31;
} DATA_SEG_DESCRIPTOR,*PDATA_SEG_DESCRIPTOR;
共有4種ENTRY格式,此處提供的是CODE SEGMENT和DATA SEGMENT的ENTRY格式. FLAT模式下的ENTRY在base_0_15,base_16_23處為0,而limit_0_15,limit_16_19處為0xfffff. granularity處為1. 表名SEGMENT地址空間是從0到0XFFFFFFFF的4G的地址空間.
b) 從SEGMENT處取出BASE ADDRESS 和LIMIT. 將要訪問的ADDRESS首先進行ACCESS CHECK,是否超出SEGMENT的限制.
c) 將要訪問的ADDRESS+BASE ADDRESS,形成需要32BIT訪問的虛擬地址. 該地址被解釋成如下格式:
typedef struct
{
UINT32 offset :12;
UINT32 page_index :10;
UINT32 pdbr_index :10;
} VA,*LPVA;
d) pdbr_index作為CR3的INDEX,獲得到一個如下定義的數據結構
typedef struct
{
UINT8 present :1;
UINT8 writable :1;
UINT8 supervisor :1;
UINT8 writethrough:1;
UINT8 cachedisable:1;
UINT8 accessed :1;
UINT8 reserved1 :1;
UINT8 pagesize :1;
UINT8 ignoreed :1;
UINT8 avl :3;
UINT8 ptadr_12_15 :4;
UINT16 ptadr_16_31;
}PDE,*LPPDE;
e) 從中取出PAGE TABLE的地址. 並且使用page_index作為INDEX,得到如下數據結構
typedef struct
{
UINT8 present :1;
UINT8 writable :1;
UINT8 supervisor :1;
UINT8 writethrough:1;
UINT8 cachedisable:1;
UINT8 accessed :1;
UINT8 dirty :1;
UINT8 pta :1;
UINT8 global :1;
UINT8 avl :3;
UINT8 ptadr_12_15 :4;
UINT16 ptadr_16_31;
}PTE,*LPPTE;
f) 從PTE中獲得PAGE的真正物理地址的BASE ADDRESS. 此BASE ADDRESS表名了物理地址的.高20位. 加上虛擬地址的offset就是物理地址所在了.
ARM系列的MMU
ARM出品的CPU,MMU作為一個協處理器存在。根據不同的系列有不同搭配。需要查詢DATASHEET才可知道是否有MMU。如果有的話,一定是編號為15的協處理器。可以提供32BIT共4G的地址空間。
ARM MMU提供的分頁機制有1K/4K/64K 3種模式. 本文介紹的是目前作業系統通常使用的4K模式。
涉及的暫存器,全部位於協處理器15.
ARM cpu地址轉換涉及三種地址:虛擬地址(VA,Virtual Address),變換後的虛擬地址(MVA,Modified Virtual Address),物理地址(PA,Physical Address)。沒有啟動MMU時,CPU核心、cache、MMU、外設等所有部件使用的都是物理地址。啟動MMU後,CPU核心對外發出的是虛擬地址VA,VA被轉換為MVA供cache、MMU使用,並再次被轉換為PA,最後使用PA讀取實際設備。
ARM沒有SEGMENT的暫存器,是真正的FLAT模式的CPU。給定一個ADDRESS,該地址可以被理解為如下數據結構:
typedef struct
{
UINT32 offset :12;
UINT32 page_index :8;
UINT32 pdbr_index :12;
} VA,*LPVA;
從MMU暫存器2中取出BIT14-31,pdbr_index就是這個表的索引,每個入口為4BYTE大小,結構為
typedef struct
{
UINT32 type :2; //always set to 01b
UINT32 writebackcacheable:1;
UINT32 writethroughcacheable:1;
UINT32 ignore :1; //set to 1b always
UINT32 domain :4;
UINT32 reserved :1; //set 0
UINT32 base_addr:22;
} PDE,*LPPDE;
獲得的PDE地址,獲得如下結構的ARRAY,用page_index作為索引,取出內容。
typedef struct
{
UINT32 type :2; //always set to 11b
UINT32 ignore :3; //set to 100b always
UINT32 domain :4;
UINT32 reserved :3; //set 0
UINT32 base_addr:20;
} PTE,*LPPTE;
從PTE中獲得的基地址和上offset,組成了物理地址.
PDE/PTE中其他的BIT,用於訪問控制。這邊講述的是一切正常,物理地址被正常組合出來的狀況。
ARM/X86 MMU使用上的差異
⒈X86始終是有SEGMENT的概念存在. 而ARM則沒有此概念(沒有SEGMENT REGISTER.).
⒉ARM有個DOMAIN的概念. 用於訪問授權. 這是X86所沒有的概念. 當通用OS嘗試同時適用於此2者的CPU上,一般會拋棄DOMAIN的使用.