在現在的工作項目中雖然沒有使用到MMU功能,但MMU是較複雜的嵌入式操作系統運行的基礎。例如Linux就不能夠運行在沒有MMU的ARM7處理器上,ucLinux就是為了适應沒有MMU的處理器而對Linux進行的裁剪和修改。了解MMU基礎知識,對理解編譯鍊接,OS多進程,嵌入式系統架構等有很好的幫助。由于該部分内容涉及到MMU硬件、CPU架構、編譯鍊接、OS等知識,學習難度較大。
關鍵字 MMU、TLB、多任務OS、地址空間、虛拟内存、虛拟地址/物理地址
學習順序 問題的引出、虛拟地址和物理地址、虛拟内存、OS進程和MMU,MMU運行
引子
早期的計算機PC,或者現在使用8位/16位MCU(單片機)的嵌入式設備,程序是直接運行在物理内存上的(SDRAM或者NOR Flash)。所謂直接運行在物理内存上,是指程序在運行時所訪問的地址都是物理地址。例如,程序計數器PC中的值就是預取指令所在的物理内存的地址值。
這種程序直接運行在物理内存上的方式簡單,但是并不适應于複雜的系統,尤其是擁有多任務的OS。我們首先看看原來的方式有哪些不足和缺陷。
于是人們就引入了虛拟内存管理(Virtual Memory Management)技術。有關虛拟内存管理(Virtual Memory Management)技術在下面會有詳細的介紹。需要說明的是,上面的幾點缺陷除了第一點之外,其它都是針對有OS的系統而言的。 虛拟内存管理技術的出現和操作系統的發展有本質的聯系。 本節可以參考《程序員的自我修養-鍊接、裝載與庫》第1章 1.5 内存不夠怎麼辦。
虛拟内存管理技術
随着計算機的發展,應用程序的規模逐漸增大,一個難題出現在程序員的面前,那就是應用程序太大以至于内存容納不下該程序,通常解決的辦法是把程序分割成許多稱為覆蓋塊(overlay)的片段。覆蓋塊0首先運行,結束時他将調用另一個覆蓋塊。雖然覆蓋塊的交換是由OS完成的,但是必須先由程序員把程序先進行分割,這是一個費時費力的工作,而且相當枯燥。人們必須找到更好的辦法從根本上解決這個問題。
人們找到了另外一個辦法,這就是虛拟内存管理(Virtual Memory Management)技術。虛拟内存管理技術的基本思想是程序,數據,堆棧的總的大小可以超過物理存儲器的大小,操作系統把當前使用的部分保留在内存中,而把其他未被使用的部分保存在磁盤上。比如對一個16MB的程序和一個内存隻有4MB的機器,OS通過選擇,可以決定各個時刻将哪4M的内容保留在内存中,并在需要時在内存和磁盤間交換程序片段,這樣就可以把這個16M的程序運行在一個隻具有4M内存機器上了。而這個16M的程序在運行前不必由程序員進行分割。需要說明的一點,操作系統的内核是常駐内存的。
圍繞着虛拟内存管理(Virtual Memory Management)技術,就産生了分頁技術,虛拟地址,地址空間,TLB,MMU等概念。
分頁技術
虛拟内存管理技術中最常見的是分頁(paging)技術,MMU就是該技術的硬件實現。虛拟地址空間劃分成稱為頁(page)的單位,而相應的物理地址空間也被進行劃分,單位是頁框(fRAMe),頁和頁框的大小必須相同。接下來配合圖片以一個例子說明頁與頁框之間在MMU的調度下是如何進行映射。
在這個例子中我們有一台可以生成16位地址的機器,它的虛拟地址範圍從0x0000~0xFFFF(64K),而這台機器隻有32K的物理内存。它可以運行64K的程序,但該程序不能一次性調入内存運行。這台機器必須有一個達到可以存放64K程序的外部存儲器(例如磁盤或是FLASH),以保證程序片段在需要時可以被調用。在這個例子中,頁的大小為4K,頁框大小與頁相同(這點是必須保證的,内存和外圍存儲器之間的傳輸總是以頁為單位的),對應64K的虛拟地址和32K的物理内存,他們分别包含了16個頁和8個頁框。
我們先根據上圖解釋一下分頁後要用到的幾個術語,在上面我們已經接觸了頁和頁框,上圖中綠色部分是物理空間,其中每一格表示一個頁框。橘黃色部分是虛拟空間,每一格表示一個頁,它由兩部分組成,分别是Frame Index(頁框索引)和位p(present 存在位),Frame Index的意義很明顯,它指出本頁是往哪個物理頁框進行映射的,位p的意義則是指出本頁的映射是否有效,如上圖,當某個頁并沒有被映射時(或稱“映射無效”,Frame Index部分為X),該位為0,映射有效則該位為1。
我們執行下面這些指令(本例子的指令不針對任何特定機型,都是僞指令)
例1:MOVE REG,0 //将地址0x0000的值傳遞進寄存器REG.
虛拟地址0将被送往MMU,MMU看到該虛地址落在頁0範圍内(頁0範圍是0到4095),從上圖我們看到頁0所映射的頁框為2(頁框2的地址範圍是8192到12287),因此MMU将該虛拟地址轉化為物理地址8192,并把地址8192送到地址總線上。内存對MMU的映射一無所知,它隻看到一個對地址8192的讀請求并執行它。MMU從而把0到4096的虛拟地址映射到8192到12287的物理地址。
例2:MOVE REG,20500
虛拟地址20500在虛頁5(虛拟地址範圍是20480到24575)距開頭20個字節處,虛頁5映射到頁框3(頁框3的地址範圍是 12288到16383),于是被映射到物理地址12288 20=12308。
通過适當的設置MMU,可以把16個頁隐射到8個頁框中的任何一個,這可以解決上面提到的第二個問題程序運行的地址不确定,但是這個方法并沒有有效的解決虛拟地址空間比物理地址空間大的問題。從上圖中我們可以看到,我們隻有8個頁框(物理地址),但我們有16個頁(虛拟地址),所以我們隻能把16個頁中的8個進行有效的映射。我們看看下面發生什麼情況。
例3:MOV REG,32780
虛拟地址32780落在頁8的範圍内,從上圖中我們看到頁8沒有被有效的進行映射(該頁被打上X),這是又會發生什麼?MMU注意到這個頁沒有被映射,于是通知CPU發生一個 缺頁故障(page fault) 。這種情況下操作系統必須處理這個頁故障,它必須從8個物理頁框中找到1個當前很少被使用的頁框并把該頁框的内容寫入外圍存儲器(這個動作被稱為page copy),把對應虛拟地址處的程序從外圍存儲器拷貝到剛才騰空的頁框中,随後把需要引用的頁(頁8)映射到剛才釋放的頁框中(這個動作稱為修改映射關系),然後從新執行産生故障的指令(MOV REG,32780)。假設操作系統決定釋放頁框1,那麼它将把頁8裝入物理地址的4-8K,并做兩處修改:首先把标記頁1未被映射(原來虛頁1是被影射到頁框1的),以使以後任何對虛拟地址4K到8K的訪問都引起頁故障而使操作系統做出适當的動作(這個動作正是我們現在在讨論的),其次把頁8對應的頁框号由X變為1,因此重新執行MOV REG,32780時,MMU将把32780映射為4108。
正如本節開頭所述,MMU是分頁技術的硬件實現。我們大緻了解了MMU在我們的機器中扮演了什麼角色以及它基本的工作内容是什麼。注意,本例中的MMU并無針對某種特定的機型,它是所有MMU工作的一個抽象。
虛拟内存管理與OS
要理解内存在程序中是如何分配的,首先需要理解操作系統如何将内存分配給程序。計算機上的每一個進程都認為自己可以訪問所有的物理内存。顯然,由于同時在運行多個程序,所以每個進程不可能擁有全部内存。實際上,這些進程使用的是虛拟内存。
舉個例子,讓我們假定您的程序正在訪問地址為 629 的内存。不過,虛拟内存系統不需要将其存儲在位置為 629 的 RAM 中。實際上,它甚至可以不在 RAM 中 ―― 如果物理RAM已經滿了,它甚至可能已經被轉移到硬盤上!由于這類地址不必反映内存所在的物理位置,所以它們被稱為虛拟内存。操作系統維持着一個虛拟地址到物理地址的轉換的表,以便計算機硬件可以正确地響應地址請求。如果地址在硬盤上而不是在RAM中,那麼操作系統将暫時停止您的進程,将其他内存轉存到硬盤中,從硬盤上加載被請求的内存, 然後再重新啟動您的進程。這樣,每個進程都獲得了自己可以使用的地址空間,可以訪問比您物理上安裝的内存更多的内存。
在32-位的X86系統上,每一個進程可以訪問4GB内存。現在,大部分人的系統上并沒有4GB内存,即使您将swap也算上, 每個進程所使用的内存也肯定少于 4 GB。因此,當加載一個進程時, 它會得到一個取決于某個稱為系統中斷點(system break)的特定地址的初始内存分配。該地址之後是未被映射的内存 ――用于在RAM或者硬盤中沒有分配相應物理位置的内存。因此,如果一個進程運行超出了它初始分配的内存,那麼它必須請求操作系統“映射進來(map in)”更多的内存。(映射是一個表示一一對應關系的數學術語 ―― 當内存的虛拟地址有一個對應的物理地址來存儲内存内容時,該内存将被映射。)
基于UNIX的系統有兩個可映射到物理内存中的基本系統調用:
如您所見, brk() 或者 mmap() 都可以用來向我們的 進程添加額外的虛拟内存。在我們的例子中将使用 brk(),因為它更簡單,更通用。
摘自Jonathan Bartlett《内存管理内幕-動态分配的選擇、折衷和實現》。
虛拟/物理地址空間
地址空間是一個抽象的概念,由CPU體系架構的地址總線決定,一般等同于CPU的尋址範圍、x位處理器中的x。地址空間一般分為 虛拟地址空間 和 物理地址空間 。
任何時候,計算機上都存在一個程序能夠訪問的地址集合,我們稱之為地址空間。這個空間的大小由CPU的位數決定,例如一個32位的CPU,它的地址範圍是0~0xFFFFFFFF (4G),而對于一個64位的CPU,它的地址範圍為0~0xFFFFFFFFFFFFFFFF (64T)。這個空間就是我們的程序能夠産生的地址範圍,我們把這個地址範圍稱為 虛拟地址空間 ,該空間中的某一個地址我們稱之為虛拟地址。與虛拟地址空間和虛拟地址相對應的則是物理地址空間和物理地址,大多數時候我們的系統所具備的物理地址空間隻是虛拟地址空間的一個子集,這裡舉一個最簡單的例子直觀地說明這兩者,對于一台内存為256MB的32bit x86主機來說,它的虛拟地址空間範圍是0~0xFFFFFFFF(4G),而物理地址空間範圍是0x000000000~0x0FFFFFFF(256MB)。
虛拟地址又被簡稱為虛地址,物理地址又被稱為實地址。虛拟地址和物理地址之間的轉換,又稱為虛實地址轉化。
在沒有使用的虛拟内存管理(Virtual Memory Management)技術機器上,虛拟地址被直接送到内存總線上,使具有相同地址的物理存儲器被讀寫。而在使用了虛拟存儲器的情況下,虛拟地址不是被直接送到内存地址總線上,而是送到内存管理單元MMU。他由一個或一組芯片組成,一般存在與協處理器中,其功能是把虛拟地址映射為物理地址。
下面這張圖就是描述虛拟地址/物理地址和CPU核、MMU、内存之間的關系。
虛拟内存管理與TLB/MMU
虛拟内存管理(Virtual Memory Management)技術中重要的一點,就是将地址空間分成了虛拟地址和物理地址,在CPU核和内存之間的地址總線上增加了一層。首先需要指出的是,TLB和MMU隻是實現虛拟地址到物理地址轉化最為常見的一種辦法,還有另外的方法,如下文所描述。
為了區分不同進程的存儲空間,現在多任務的操作系統以及處理器都需要支持虛拟地址(Virtual Address, VA)物理地址(Physical Address, PA)轉化,地址轉換主要分為兩種:
摘自《處理器的存儲子系統》by Sigma。直接映射的方式在MIPS内存模型中的kseg0和kseg1就是使用的這種辦法,直接将虛拟地址的高1位或者3位去掉,轉化成物理地址。參考和《See MIPS Run》p47。
下面我們隻介紹使用MMU和TLB的映射原理。
MMU工作原理
MMU是如何把VA映射成PA的呢?内存中保存一張VA轉PA的表,MMU被配置為指向該表的開始的物理地址,給一個VA通過MMU查表就可以查到PA,這是從簡單處理解。實際上并不是這麼簡單,通常要有一個多級的查表過程,對于ARM體系結構是兩級查表,對于一些64位體系結構則需要更多級。MMU的工作原理和機制,參考下面的各處理器架構MMU實例章節。
X86的MMU實例
以32位的x86系統為例。
x86采用兩級頁表結構,第一級稱為頁目錄表,第二級稱為頁表。
名稱類型VA索引bit位頁表項存儲大小/KB默認頁大小支持的頁大小頁目錄表L110位 [31:22]10244xxxxxx頁表L210位 [21:12]102444KBxxx
頁表目錄的起始物理地址由CPU中的CR3寄存器指定。ARM920T的MMU實例
重點參考《ARM嵌入式系統開發-軟件設計與優化》第14章 存儲管理單元。下面的使用的截圖是三星公司的S3C2410,這款芯片是一種很常見的采用ARM920T的芯片。
ARM920T的MMU和Cache都集成在CP15協處理器中,MMU和Cache的聯系非常密切。
下面是CP15協處理器的寄存器列表,和CPU核的r0到r15寄存器一樣,協處理器寄存器也是用0到15來編号,在指令中用4個bit來表示寄存器編号,有些協處理器寄存器有影子寄存器。
CP15:c2寄存器保存轉化表基地址TTB(Translation Table Base address)――指向L1主頁表在物理内存中的位置。TTB地址必須與存儲器的16KB邊界對齊(寄存器的bit0 - bit13 為0,should be zero)。
對CP15協處理器的操作使用mcr和mrc兩條協處理器指令,這兩條指令的記法是從後往前看:mcr是把r(CPU核寄存器)中的數據傳送到c(協處理器寄存器)中,mrc則是把c(協處理器寄存器)中的數據傳送到r(CPU核寄存器)中。對CP15協處理器的所有操作都是通過CPU核寄存器和CP15寄存器之間交換數據來完成的。
Translation Table Walk
這個過程稱為Translation Table Walk,Walk這個詞用得非常形象。從TTB走到一級頁表,又走到二級頁表,又走到物理頁面, 一次尋址其實是三次訪問物理内存。 注意這個“走”的過程完全是硬件做的,每次CPU尋址時MMU就自動完成以上四步,不需要編寫指令指示MMU去做,前提是操作系統要維護頁表項的正确性,每次分配内存時填寫相應的頁表項,每次釋放内存時清除相應的頁表項,在必要的時候分配或釋放整個頁表。
ARM MMU
ARM MMU硬件采用2級頁表結構:一級頁表(L1)和二級頁表(L2)。
L1頁表隻有一個主頁表,也稱為L1主頁表(L1 master page table)或者段頁表(section page table)。使用VA的高12bit位索引該表,所以該表有2的12次方(4K)個頁表項(PTE,page table entry),每個頁表項4個字節,一共需要占去内存16KB。
有兩種類型的L2頁表,分别是L2粗頁表(coarse page table)和L2細頁表(fine page table)。對于L2粗頁表,使用VA的次高8bit索引該表,所以該表有2的8次方(256)個頁表項(PTE,page table entry),每個頁表項4個字節,一共需要占去内存1KB。對于L2細頁表,使用VA的次高10bit索引該表,所以該表有2的10次方(1024)個頁表項(PTE,page table entry),每個頁表項4個字節,一共需要占去内存4KB。
名稱類型VA索引bit位頁表項存儲大小/KB默認頁大小支持的頁大小主頁表/段頁表L112位 [31:20]4096161MB1MB細頁表L210位 [19:10]102441KB1/4/64KB粗頁表L28位 [19:12]25614KB4/64KB
下圖是使用L1主頁表和粗頁表實現的一個簡單VA到PA的轉化示意圖:
多級頁表的意義
對于OS來說,頁表是進程私有的,因此各個進程的頁表被放置在不同的物理内存中。在進程啟動或者切換時,都必須把進程的頁表的起始物理地址告訴MMU,也就是上面提到的TTB值(CP15:c2寄存器)。下面看采用單級頁表的情況。假設頁的大小為4KB,那麼對于頁表的索引就需要使用VA的[31:12],也就有1M個頁表項,一個頁表項4字節,可以算的頁表的大小為4M。 這意味着這4M空間必須作為進程的必備資源在啟動的時候一次分配,而且這4M的内存必須在物理地址上連續。 這和虛拟内存設計的理念(小内存系統上運行盡量多的程序)不相符合。解決的辦法就是采用多級頁表。
多級頁表是如何解決單級頁表存在的問題呢?考慮到一個進程不需要同時訪問到4GB的内存,因此把頁表也分散開來,一級頁表必須分配,二級頁表像物理頁面那樣在需要的時候再分配映射。當然,多級頁表需要MMU硬件支持。
MIPS32的MMU實例
待續
MMU和Cache的關系
待續
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!