天天看點

ARM MMU 詳解一、MMU 的産生二、 MMU工作過程三、S3C24XX 的 MMU 工作過程

一、MMU 的産生

許多年以前,當人們還在使用DOS或是更古老的作業系統的時候,計算機的記憶體還非常小,一般都是以 K 為機關進行計算,相應的,當時的程式規模也不大,是以記憶體容量雖然小,但還是可以容納當時的程式。

但随着圖形界面的興起,還有使用者需求的不斷增大,應用程式的規模也随之膨脹起來,終于一個難題出現在程式員的面前,那就是應用程式太大以至于記憶體容納不下該程式,通常解決的辦法是把程式分割成許多稱為覆寫塊(overlay)的片段。覆寫塊 0 首先運作,結束時他将調用另一個覆寫塊。雖然覆寫塊的交換是由 OS 完成的,但是必須先由程式員把程式先進行分割,這是一個費時費力的工作,而且相當枯燥。人們必須找到更好的辦法從根本上解決這個問題。

不久人們找到了一個辦法,這就是虛拟存儲器(virtual memory).。虛拟存儲器的基本思想是程式,資料,堆棧的總的大小可以超過實體存儲器的大小,作業系統把目前使用的部分保留在記憶體中,而把其他未被使用的部分儲存在磁盤上。比如對一個 16MB 的程式和一個記憶體隻有 4MB 的機器,OS 通過選擇,可以決定各個時刻将哪 4M 的内容保留在記憶體中,并在需要時在記憶體和磁盤間交換程式片段,這樣就可以把這個 16M 的程式運作在一個隻具有 4M 記憶體機器上了。而這個 16M 的程式在運作前不必由程式員進行分割。

任何時候,計算機上都存在一個程式能夠産生的位址集合,我們稱之為位址範圍。這個範圍的大小由 CPU 的位數決定,例如一個 32 位的 CPU,它的位址範圍是 0 ~ 0xFFFFFFFF (4G)。而對于一個 64 位的 CPU,它的位址範圍為 0 ~ 0xFFFFFFFFFFFFFFFF (64T), 這個範圍就是我們的程式能夠産生的位址範圍,我們把這個位址範圍稱為虛拟位址空間,該空間中的某一個位址我們稱之為虛拟位址。與虛拟位址空間和虛拟位址相對應的則是實體位址空間和實體位址,大多數時候我們的系統所具備的實體位址空間隻是虛拟位址空間的一個子集,這裡舉一個最簡單的例子直覺地說明這兩者,對于一台記憶體為 256MB 的 32 bit x86 主機來說,它的虛拟位址空間範圍是0 ~ 0xFFFFFFFF(4G), 而實體位址空間範圍是 0x000000000 ~ 0x0FFFFFFF(256MB)。

在沒有使用虛拟存儲器的機器上,虛拟位址被直接送到記憶體總線上,使具有相同位址的實體存儲器被讀寫。而在使用了虛拟存儲器的情況下,虛拟位址不是被直接送到記憶體位址總線上,而是送到記憶體管理單元—— MMU(主角終于出現了)。他由一個或一組晶片組成,一般存在于協處理器中,其功能是把虛拟位址映射為實體位址。

二、 MMU工作過程

大多數使用虛拟存儲器的系統都使用一種稱為分頁(paging)。虛拟位址空間劃分成稱為頁(page)的機關,而相應的實體位址空間也被進行劃分,機關是頁框(frame). 頁和頁框的大小必須相同。接下來配合圖檔我以一個例子說明頁與頁框之間在 MMU 的排程下是如何進行映射的:

ARM MMU 詳解一、MMU 的産生二、 MMU工作過程三、S3C24XX 的 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 //将 0 号位址的值傳遞進寄存器 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,8192
           

被轉換為

MOVE REG,24576
           

因為虛拟位址 8192 在頁 2 中,而頁 2 被映射到頁框 6(實體位址從 24576 到 28671)。

例3:

MOVE REG,20500
           

被轉換為

MOVE REG,12308
           

虛拟位址 20500 在虛頁 5(虛拟位址範圍是 20480 到 24575 )距開頭 20 個位元組處,虛頁 5 映射到頁框 3(頁框 3 的位址範圍是 12288 到 16383),于是被映射到實體位址 12288+ 20=12308。

通過适當的設定 MMU,可以把 16 個虛頁映射到 8 個頁框中的任何一個,但是這個方法并沒有有效的解決虛拟位址空間比實體位址空間大的問題。從上圖中我們可以看到,我們隻有 8 個頁框(實體位址),但我們有 16 個頁(虛拟位址),是以我們隻能把 16 個頁中的 8 個進行有效的映射。我們看看例 4 會發生什麼情況。

例 4:

虛拟位址 32780 落在頁 8 的範圍内,從上圖中我們看到,頁 8 沒有被有效的進行映射(該頁被打上X),這時又會發生什麼?MMU 注意到這個頁沒有被映射,于是通知 CPU 發生一個缺頁故障(page fault).這種情況下作業系統必須處理這個頁故障,它必須從 8 個實體頁框中,找到 1 個目前很少被使用的頁框,并把該頁框的内容寫入外圍存儲器(這個動作被稱為 page copy),随後把需要引用的頁(即虛拟位址空間的 頁 8)映射到剛才釋放的頁框中(這個動作稱為修改映射關系),然後重新執行産生故障的指令(MOV REG, 32780)。

假設作業系統決定釋放頁框 1,那麼它将把虛頁 8 裝入實體位址的 4-8 K 位址範圍, 并做兩處修改:首先把标記虛頁1未被映射(原來虛頁1是被映射到頁框 1 的),以使以後任何對虛拟位址 4K ~ 8K的通路都引起頁故障,而使作業系統做出适當的動作(這個動作正是我們現在在讨論的),其次他把虛頁 8 對應的頁框号由 X 變為 1,是以重新執行 MOV REG,32780 時,MMU 将把 32780映射為 4108 。

我們大緻了解了 MMU 在我們的機器中扮演了什麼角色以及它基本的工作内容是什麼,下面我們将舉例子說明它究竟是如何工作的(注意,本例中的 MMU 并無針對某種特定的機型,它是所有 MMU 工作的一個抽象)。

首先明确一點,MMU 的主要工作隻有一個,就是把虛拟位址映射到實體位址。

我們已經知道,大多數使用虛拟存儲器的系統都使用一種稱為分頁(paging)的技術,就象我們剛才所舉的例子,虛拟位址空間被分成大小相同的一組頁,每個頁有一個用來标示它的頁号(這個頁号一般是它在該組中的索引,這點和C/C++中的數組相似)。在上面的例子中0 ~ 4K 的頁号為 0,4 ~ 8K的頁号為 1,8 ~ 12K 的頁号為 2,以此類推。

而虛拟位址(注意:是一個确定的位址,不是一個空間)被 MMU 分為 2 個部分,第一部分是頁号索引(page Index),第二部分則是相對該頁首位址的偏移量(offset)。

我們還是以剛才那個 16 位機器結合下圖進行一個執行個體說明。該執行個體中,虛拟位址 8196被送進 MMU, MMU 把它映射成實體位址。16 位的 CPU 總共能産生的位址範圍是0 ~ 64K,按每頁 4K 的大小計算,該空間必須被分成 16 個頁。而我們的虛拟位址第一部分所能夠表達的範圍也必須等于 16(這樣才能索引到該頁組中的每一個頁),也就是說這個部分至少需要 4 個 bit。一個頁的大小是 4K(4096), 也就是說偏移部分必須使用 12 個 bit來表示( 2^12=4096,這樣才能通路到一個頁中的所有位址), 8196 的二進制碼如下圖所示:

ARM MMU 詳解一、MMU 的産生二、 MMU工作過程三、S3C24XX 的 MMU 工作過程

該位址的頁号索引為 0010(二進制碼),既索引的頁為頁 2,第二部分為000000000100(二進制),偏移量為 4。頁 2 中的頁框号為 6(頁 2 映射在頁框 6,見上圖),我們看到頁框6的實體位址是 24 ~ 28K。于是 MMU 計算出虛拟位址 8196 應該被映射成實體位址 24580(頁框首位址+偏移量=24576+4=24580)。

同樣的,若我們對虛拟位址 1026 進行讀取,1026 的二進制碼為 0000010000000010,page index=”0000”=0, offset=010000000010=1026。頁号為 0,該頁映射的頁框号為2,頁框 2 的實體位址範圍是 8192~12287,故 MMU 将虛拟位址 1026 映射為實體位址 9218(頁框首位址+偏移量= 8192+1026 =9218)。以上就是MMU的工作過程。

三、S3C24XX 的 MMU 工作過程

下面我們針對 s3c2410 的 MMU(注1) 進行講解。

S3c2410 總共有 4 種記憶體映射方式,分别是:

  1. Fault (無映射)
  2. Coarse Page (粗表)
  3. Section (段)
  4. Fine Page (細表)

我們以 Section (段)進行說明。

ARM920T 是一個 32bit 的 CPU,它的虛拟位址空間為 2^32=4G 。而在 Section 模式,這 4G 的虛拟空間被分成一個一個稱為段(Section)的機關(與我們上面講的頁在本質上其實是一緻的), 每個段的長度是 1M (而我們之前所使用的頁的長度是 4K)。4G 的虛拟記憶體總共可以被分成 4096 個段(1M*4096=4G),是以我們必須用 4096 個描述符來對這組段進行描述,每個描述符占用 4 個Byte, 故這組描述符的大小為 16KB (4byte * 4096),這4096 個描述符構為一個表格,我們稱其為 Tralaton Table.

ARM MMU 詳解一、MMU 的産生二、 MMU工作過程三、S3C24XX 的 MMU 工作過程

上圖是描述符的結構。

Section base address:段基位址(相當于頁框号首位址);

AP:通路控制位 Access Permission;

Domain:通路控制寄存器的索引。Domain 與 AP 配合使用,對通路權限進行檢查;

C:當 C 被置 1 時為 write-through (WT)模式;

B:當 B 被置 1 時為 write-back (WB)模式(C,B兩個位在同一時刻隻能有一個被置1)。

下面是 s3c2410 記憶體映射後的一個示意圖:

ARM MMU 詳解一、MMU 的産生二、 MMU工作過程三、S3C24XX 的 MMU 工作過程

我的 s3c2410 上配置的 SDRSAM 大小為 64M,該 SDRAM 的實體位址範圍是 0x3000_ 0000 ~ 0x33FF_FFFF(屬于 Bank 6),由于 1 個 Section 的大小是 1M,是以該實體空間可以被分成 64 個實體段(頁框)。

在 Section 模式下,送進 MMU 的虛拟位址(注1)被分為兩部分(這點和我們上面舉的例子是一樣的),這兩部分為 Descriptor Index(相當于上面例子的 Page Index )和 Offset,Descript index 長度為 12 bit (2^12=4096,從這個關系式你能看出什麼?:) ),Offset 長度為 20 bit(2^20= 1M,你又能看出什麼?:))。

觀察一下一個描述符(Descriptor)中的 Section Base Address 部分,它長度為 12 bit,裡面的值是該虛拟段(頁)映射成的實體段(頁框)的實體位址前 12 bit,由于每一個實體段的長度都是 1 M,是以實體段首位址的後 20 bit 總是為 0x00000(每個 Section 都是以 1M 對齊),确定一個實體位址的方法是: 實體頁框基位址+虛拟位址中的偏移部分= Section Base Address << 20 + Offset 。呵呵,可能你有點糊塗了,還是舉一個實際例子說明吧。

假設現在執行指令

MOV REG, 0x30000012

,虛拟位址的二進制碼為 00110000 00000000 00000000 00010010,前 12 位是 Descriptor Index= 00110000 0000 = 768,故在Translation Table 裡面找到第 768 号描述符,該描述的 Section Base Address = ”0x0300 ”,也就是說描述符所描述的虛拟段(頁)所映射的實體段(頁框)的首位址為0x3000 0000(實體段(頁框)的基位址= Section Base Address 左移 20 bit = 0x0300 << 20 = 0x3000 0000),而 Offset = 000000 00000000 00010010 = 0x12,故虛拟位址 0x30000012 映射成的實體位址 = 0x3000 0000 + 0x12 = 0x3000 0012(實體頁框基位址+虛拟位址中的偏移)。你可能會問怎麼這個虛拟位址和映射後的實體位址一樣?這是由我們定義的映射規則所決定的。在這個例子中我們定義的映射規則是把虛拟位址映射成和他相等的實體位址。我們這樣書寫映射關系的代碼:

void mem_mapping_linear(void)
{
    unsigned long descriptor_index, section_base, sdram_base, sdram_size;
    sdram_base=0x30000000;
    sdram_size=0x04000000;
    for (section _base= sdram_base,descriptor_index = section _base>>20;
          section _base < sdram_base+ sdram_size;
          descriptor_index+=1;section _base +=0x100000)
    {
        *(mmu_tlb_base + (descriptor_index)) = (section _base>>20) |  
                         MMU_OTHER_SECDESC;
    }
}
           

上面的這段段代碼,把虛拟空間 0x3000 0000 ~ 0x33FF FFFF 映射到實體空間 0x3000 0000 ~ 0x33FF FFFF,由于虛拟空間與實體空間空間相吻合,是以虛拟位址與他們各自對應的實體位址在值上是一緻的。當初始完 Translation Table 之後,記得要把Translation Table 的首位址(第 0 号描述符的位址)加載進協處理器 CP15 的 Control Register 2 ( 2 号控制寄存器)中,該控制寄存器的名稱叫做 Translation table base (TTB) register。

以上讨論的是 descriptor 中的 Section Base Address 以及虛拟位址和實體位址的映射關系,然而 MMU 還有一個重要的功能,那就是通路控制機制(Access Permission )。簡單說通路控制機制就是,CPU 通過某種方法判斷目前程式對記憶體的通路是否合法(是否有權限對該記憶體進行通路),如果目前的程式并沒有權限對即将通路的記憶體區域進行操作,則 CPU 将引發一個異常,s3c2410 稱該異常為 Permission fault,x86 架構則把這種異常稱之為通用保護異常(General Protection),什麼情況會引起 Permission fault呢 ?比如處于 User 級别的程式要對一個 System 級别的記憶體區域進行寫操作,這種操作是越權的,應該引起一個 Permission fault,搞過 x86 架構的朋友應該聽過保護模式(Protection Mode),保護模式就是基于這種思想進行工作的,于是我們也可以這麼說:s3c2410 的通路控制機制其實就是一種保護機制。那 s3c2410 的通路控制機制到底是由什麼元素去參與完成的呢?它們間是怎麼協調工作的呢?這些元素總共有:

  1. 協處理器 CP15 中 Control Register3:DOMAIN ACCESS CONTROL REGISTER;
  2. 段描述符中的 AP 位和 Domain 位;
  3. 協處理器 CP15 中 Control Register 1(控制寄存器 1)中的 S bit 和 R bit;
  4. 協處理器 CP15 中 Control Register 5(控制寄存器 5);
  5. 協處理器 CP15 中 Control Register 6(控制寄存器 6);

DOMAIN ACCESS CONTROL REGISTER 是通路控制寄存器,該寄存器有效位為 32,被分成 16 個區域,每個區域由兩個位組成,他們說明了目前記憶體的通路權限檢查的級别,如下圖所示:

ARM MMU 詳解一、MMU 的産生二、 MMU工作過程三、S3C24XX 的 MMU 工作過程

每區域可以填寫的值有 4 個,分别為 00,01,10,11(二進制),他們的意義如下所示:

ARM MMU 詳解一、MMU 的産生二、 MMU工作過程三、S3C24XX 的 MMU 工作過程

00:目前級别下,該記憶體區域不允許被通路,任何的通路都會引起一個 domain fault;

01:目前級别下,該記憶體區域的通路必須配合該記憶體區域的段描述符中 AP 位進行權檢查;

10:保留狀态(我們最好不要填寫該值,以免引起不能确定的問題);

11:目前級别下,對該記憶體區域的通路都不進行權限檢查。

我們再來看看 discriptor 中的 Domain 區域,該區域總共有 4 個bit,裡面的值是對DOMAIN ACCESS CONTROL REGISTER 中 16 個區域的索引。而 AP 位配合 S bit 和 A bit 對目前描述符描述的記憶體區域被通路權限的說明,他們的配合關系如下圖所示:

ARM MMU 詳解一、MMU 的産生二、 MMU工作過程三、S3C24XX 的 MMU 工作過程

AP 位也是有四個值,結合執行個體對其進行說明。

在下面的例子中,我們的 DOMAIN ACCESS CONTROL REGISTER 都被初始化成0xFFFF BDCF,如下圖所示:

ARM MMU 詳解一、MMU 的産生二、 MMU工作過程三、S3C24XX 的 MMU 工作過程

例 1:

Discriptor 中的 domain=4,AP=10(這種情況下 S bit ,A bit 被忽略)。

假設現在我要對該描述符描述的記憶體區域進行通路:

由于domain=4,而DOMAIN ACCESS CONTROL REGISTER中field 4的值是01,系統會對該通路進行通路權限的檢查。

假設目前CPU處于Supervisor模式下,則程式可以對該描述符描述的記憶體區域進行讀寫操作。

假設目前CPU處于User模式下,則程式可以對該描述符描述的記憶體進行讀通路,若對其進行寫操作則引起一個permission fault.

例 2:

Discriptor 中的domain=0,AP=10(這種情況下S bit ,A bit 被忽略)

domain=0,而DOMAIN ACCESS CONTROL REGISTER中field 0的值是11,系統對任何記憶體區域的通路都不進行通路權限的檢查。

由于統對任何記憶體區域的通路都不進行通路權限的檢查,是以無論CPU處于合種模式下(Supervisor模式或是User模式),程式對該描述符描述的記憶體都可以順利地進行讀寫操作

例 3:

Discriptor 中的domain=4,AP=11(這種情況下S bit ,A bit 被忽略)

由于domain=4,而DOMAIN ACCESS CONTROL REGISTER中field 4的值是01,系統會對該通路進行通路權限的檢查。

由于AP=11,是以無論CPU處于合種模式下(Supervisor模式或是User模式),程式對該描述符描述的記憶體都可以順利地進行讀寫操作

例 4:

Discriptor 中的domain=4,AP=00, S bit=”0”,A bit=”0”

由于domain=4,而DOMAIN ACCESS CONTROL REGISTER中field 4的值是01,系統會對該通路進行通路權限的檢查。

由于AP=00,S bit=”0”,A bit=”0”,是以無論CPU處于合種模式下(Supervisor模式或是User模式),程式對該描述符描述的記憶體都隻能進行讀操作,否則引起permission fault.

通過以上4個例子我們得出兩個結論:

1.對某個記憶體區域的通路是否需要進行權限檢查是由該記憶體區域的描述符中的Domain域決定的。

2.某個記憶體區域的通路權限是由該記憶體區域的描述符中的AP位和協處理器CP15中Control Register1(控制寄存器1)中的S bit和R bit所決定的。

原文連結