閱讀704 返回首頁    go 微軟 go windows


討論MMU

MMU是Memory Management Unit的縮寫,中文名是內存管理單元,它是中央處理器(CPU)中用來管理虛擬存儲器、物理存儲器的控製線路,同時也負責虛擬地址映射為物理地址,以及提供硬件機製的內存訪問授權。

一、MMU的曆史

  許多年以前,當人們還在使用DOS或是更古老的操作係統的時候,計算機的內存還非常小,一般都是以K為單位進行計算,相應的,當時的程序規模也不大,所以內存容量雖然小,但還是可以容納當時的程序。但隨著圖形界麵的興起還有用戶需求的不斷增大,應用程序的規模也隨之膨脹起來,終於一個難題出現在程序員的麵前,那就是應用程序太大以至於內存容納不下該程序,通常解決的辦法是把程序分割成許多稱為覆蓋塊(overlay)的片段。覆蓋塊0首先運行,結束時他將調用另一個覆蓋塊。雖然覆蓋塊的交換是由OS完成的,但是必須先由程序員把程序先進行分割,這是一個費時費力的工作,而且相當枯燥。人們必須找到更好的辦法從根本上解決這個問題。不久人們找到了一個辦法,這就是虛擬存儲器(virtual memory).虛擬存儲器的基本思想是程序,數據,堆棧的總的大小可以超過物理存儲器的大小,操作係統把當前使用的部分保留在內存中,而把其他未被使用的部分保存在磁盤上。比如對一個16MB的程序和一個內存隻有4MB的機器,操作係統通過選擇,可以決定各個時刻將哪4M的內容保留在內存中,並在需要時在內存和磁盤間交換程序片段,這樣就可以把這個16M的程序運行在一個隻具有4M內存機器上了。而這個16M的程序在運行前不必由程序員進行分割。

二、MMU的相關概念

  ——地址範圍、虛擬地址映射為物理地址 以及 分頁機製

  任何時候,計算機上都存在一個程序能夠產生的地址集合,我們稱之為地址範圍。這個範圍的大小由CPU的位數決定,例如一個32位的CPU,它的地址範圍是0~0xFFFFFFFF (4G),而對於一個64位的CPU,它的地址範圍為0~0xFFFFFFFFFFFFFFFF (64T).這個範圍就是我們的程序能夠產生的地址範圍,我們把這個地址範圍稱為虛擬地址空間,該空間中的某一個地址我們稱之為虛擬地址。與虛擬地址空間和虛擬地址相對應的則是物理地址空間和物理地址,大多數時候我們的係統所具備的物理地址空間隻是虛擬地址空間的一個子集。這裏舉一個最簡單的例子直觀地說明這兩者,對於一台內存為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的功能

  

1、將虛擬地址映射為物理地址


   現代的多用戶多進程操作係統,需要MMU, 才能達到每個用戶進程都擁有自己獨立的地址空間的目標。使用MMU, 操作係統劃分出一段地址區域, 在這塊地址區域中, 每個進程看到的內容都不一定一樣。例如MICROSOFT WINDOWS操作係統將地址範圍4M-2G劃分為用戶地址空間,進程A在地址0X400000(4M)映射了可執行文件,進程B同樣在地址0X400000(4M)映射了可執行文件,如果A進程讀地址0X400000, 讀到的是A的可執行文件映射到RAM的內容,而進程B讀取地址0X400000時,則讀到的是B的可執行文件映射到RAM的內容。

  這就是MMU在當中進行地址轉換所起的作用。

  

2、提供硬件機製的內存訪問授權


   多年以來,微處理器一直帶有片上存儲器管理單元(MMU),MMU能使單個軟件線程工作於硬件保護地址空間。但是在許多商用實時操作係統中,即使係統中含有這些硬件也沒采用MMU。

  當應用程序的所有線程共享同一存儲器空間時,任何一個線程將有意或無意地破壞其它線程的代碼、數據或堆棧。異常線程甚至可能破壞內核代碼或內部數據結構。例如線程中的指針錯誤就能輕易使整個係統崩潰,或至少導致係統工作異常。

  就安全性和可靠性而言,基於進程的實時操作係統(RTOS)的性能更為優越。為生成具有單獨地址空間的進程,RTOS隻需要生成一些基於RAM的數據結構並使MMU加強對這些數據結構的保護。基本思路是在每個關聯轉換中“接入”一組新的邏輯地址。MMU利用當前映射,將在指令調用或數據讀寫過程中使用的邏輯地址映射為存儲器物理地址。MMU還標記對非法邏輯地址進行的訪問,這些非法邏輯地址並沒有映射到任何物理地址。

  這些進程雖然增加了利用查詢表訪問存儲器所固有的係統開銷,但其實現的效益很高。在進程邊界處,疏忽或錯誤操作將不會出現,用戶接口線程中的缺陷並不會導致其它更關鍵線程的代碼或數據遭到破壞。目前在可靠性和安全性要求很高的複雜嵌入式係統中,仍然存在采無存儲器保護的操作係統的情況,這實在有些不可思議。

  采用MMU還有利於選擇性地將頁麵映射或解映射到邏輯地址空間。物理存儲器頁麵映射至邏輯空間,以保持當前進程的代碼,其餘頁麵則用於數據映射。類似地,物理存儲器頁麵通過映射可保持進程的線程堆棧。RTOS可以在每個線程堆棧解映射之後,很容易地保留邏輯地址所對應的頁麵內容。這樣,如果任何線程分配的堆棧發生溢出,將產生硬件存儲器保護故障,內核將掛起該線程,而不使其破壞位於該地址空間中的其它重要存儲器區,如另一線程堆棧。這不僅在線程之間,還在同一地址空間之間增加了存儲器保護。

  存儲器保護(包括這類堆棧溢出檢測)在應用程序開發中通常非常有效。采用了存儲器保護,程序錯誤將產生異常並能被立即檢測,它由源代碼進行跟蹤。如果沒有存儲器保護,程序錯誤將導致一些細微的難以跟蹤的故障。實際上,由於在扁平存儲器模型中,RAM通常位於物理地址的零頁麵,因此甚至NULL指針引用的解除都無法檢測到。

四、MMU和CPU

  

1、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 PREFIX來改變當前尋址指令的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就是物理地址所在了.

  

2、ARM係列的MMU


   ARM出品的CPU,MMU作為一個協處理器存在。根據不同的係列有不同搭配。需要查詢DATASHEET才可知道是否有MMU。如果有的話,一定是編號為15的協處理器。可以提供32BIT共4G的地址空間。

  ARM MMU提供的分頁機製有1K/4K/64K 3種模式. 本文介紹的是目前操作係統通常使用的4K模式。

  涉及的寄存器, 全部位於協處理器15.

  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使用上的差異

  1、X86始終是有SEGMENT的概念存在. 而ARM則沒有此概念(沒有SEGMENT REGISTER.).

  2、ARM有個DOMAIN的概念. 用於訪問授權. 這是X86所沒有的概念. 當通用OS嚐試同時適用於此2者的CPU上, 一般會拋棄DOMAIN的使用.

最後更新:2017-04-03 14:53:37

  上一篇:go Android入門:增刪改查通訊錄
  下一篇:go [曆年IT筆試題]2014迅雷校園招聘筆試題