一、前言
在 嵌入式Linux 開發(fā)中,往往會(huì)聽到 MMU 這個(gè)詞,但大多數(shù)情況下并不會(huì)去了解它,因?yàn)椴僮飨到y(tǒng)已經(jīng)做好了關(guān)于 MMU 的一切操作,我們只需要在操作系統(tǒng)的框架下直接使用即可。但了解 MMU 有助于幫助我們理解操作系統(tǒng),理解進(jìn)程等,讓我們對(duì) 嵌入式Linux 的理解上升一個(gè)層次。本文將簡(jiǎn)單地講述一下關(guān)于 MMU 的基本信息。
注意:本文將按照ARMv7的二級(jí)頁(yè)表映射進(jìn)行講述
二、MMU
2.1 MMU基本信息
MMU 全稱為 Memory Management Unit,即 內(nèi)存管理單元。在 帶有MMU的嵌入式Linux 中,CPU 訪問(wèn)的地址都是 虛擬地址,而 MMU 負(fù)責(zé)將程序中 代碼或數(shù)據(jù) 的 虛擬地址 翻譯為 物理地址,以便程序訪問(wèn)內(nèi)存。
在執(zhí)行操作時(shí),MMU 會(huì)自動(dòng)轉(zhuǎn)換 CPU發(fā)出的虛擬地址,無(wú)法人工進(jìn)行操作,只需要配置好 MMU 相關(guān)屬性即可。
虛擬地址 是在 編譯和鏈接 時(shí)定義的,可以簡(jiǎn)單地理解為 由鏈接器和鏈接器腳本 指定虛擬地址。
除了 翻譯虛擬地址,MMU 還可以配置 內(nèi)存區(qū)域 的各項(xiàng)配置,如內(nèi)存區(qū)域的訪問(wèn)權(quán)限,內(nèi)存區(qū)域是否使能cache等功能。
總結(jié) MMU 的功能,如下:
- 翻譯虛擬地址
- 配置內(nèi)存區(qū)域的相關(guān)屬性
2.2 MMU基本概念
看到 MMU 的相關(guān)文章時(shí),總會(huì)提及幾個(gè)概念如 頁(yè),頁(yè)框(頁(yè)幀),頁(yè)表,頁(yè)表項(xiàng),TLB等等,下面我們逐個(gè)拆分來(lái)講述。
2.2.1 頁(yè)
MMU 管理 虛擬地址空間 時(shí),是按照 頁(yè) 為單位來(lái)進(jìn)行管理。在 ARMv7 的 MMU ,頁(yè)大小 一共有 16M(Super Section)、1M(Section) 、64K(Large Page) 4K(Page)。頁(yè)大小 可以通過(guò) 協(xié)處理器CP15 進(jìn)行配置,越小的頁(yè)意味著內(nèi)存的顆粒度越小,內(nèi)存使用時(shí)的浪費(fèi)會(huì)越小,但也意味著使用的TLB行越多。越大的也內(nèi)存的顆粒度月大,內(nèi)存的使用浪費(fèi)也可能月大,但使用的TLB行越少。比如只需要申請(qǐng) 7K 大小的 物理內(nèi)存,如果使用 7K大小 的內(nèi)存,我們可以分配 2 個(gè) 4K頁(yè),如果分配 64K的大頁(yè),則浪費(fèi)的空間就比較大。
2.2.2 頁(yè)框
因?yàn)?虛擬地址空間 需要有所對(duì)應(yīng)的 物理地址,這樣才能在 虛擬地址 中存儲(chǔ)數(shù)據(jù)。所以 MMU 管理 物理地址空間 時(shí),按照 頁(yè)幀 為單位進(jìn)行管理。其大小分為 64K 和 4K。一段 虛擬地址空間 有可能存在著多個(gè) 頁(yè),這些 頁(yè) 對(duì)應(yīng)著多個(gè) 頁(yè)幀。
按照筆者理解,頁(yè) 和 頁(yè)幀 是 不同地址空間下的關(guān)于內(nèi)存空間大小的概念。
2.2.3 頁(yè)表及頁(yè)表項(xiàng)
MMU 在進(jìn)行 地址轉(zhuǎn)換 時(shí),需要一些信息,存放這些信息的就是 頁(yè)表。每個(gè) 頁(yè)表 的最小單位就是 頁(yè)表項(xiàng)。
頁(yè)表 存儲(chǔ)在 物理地址空間 中,且一個(gè) 頁(yè)表項(xiàng) 對(duì)應(yīng)著一個(gè) 頁(yè)。
在 切換頁(yè)表 時(shí),通過(guò)將 頁(yè)表的物理首地址 設(shè)置到 協(xié)處理器CP15 中的 TTBR寄存器(Translation Table Base Register)。此后 MMU 會(huì)通過(guò)該地址自動(dòng)去 物理地址空間 中找到對(duì)應(yīng)的 頁(yè)表,從而完成 虛擬地址到物理地址的映射。
在不考慮 TLB 和 多級(jí)頁(yè)表 的情況下,可以簡(jiǎn)單地如下圖所示:

2.2.4 TLB
TLB 全程為 Translation Lookaside Buffer,即 旁路轉(zhuǎn)換緩沖。它是 MMU 的專屬 全相聯(lián)cache,用于臨時(shí)存放 虛擬地址到物理地址映射 所需要的信息。
下面按照步驟說(shuō)明 TLB 的作用:
- CPU 訪問(wèn) 虛擬地址 到 MMU。
- MMU 根據(jù)規(guī)則(規(guī)則在下文講述)查看 虛擬地址 是否在 TLB 中。
- 如果在 TLB 中,則稱為 TLB命中。從 TLB 中直接獲取 物理地址 對(duì)內(nèi)存進(jìn)行訪問(wèn)
- 如果不在 TLB 中,則稱為 TLB失效。此時(shí) MMU 將進(jìn)行 translation table walking,即通過(guò) 訪問(wèn)頁(yè)表來(lái)獲取 物理地址。并將該 虛擬地址 的信息存入 TLB,以便下次使用。
值得注意的是:ARM架構(gòu)的TLB只存儲(chǔ)有效的頁(yè)表項(xiàng),對(duì)于無(wú)效的頁(yè)表項(xiàng)TLB并不會(huì)存儲(chǔ)
TLB 由許多 TLB行 組成,如下圖所示:

TLB行 由 3個(gè) 部分組成,分別為 標(biāo)簽、ASID 和 描述符
- 標(biāo)簽:該部分由 虛擬地址的一部分bit 組成,MMU 通過(guò)將 虛擬地址的一部分bit 和 TLB 的所有標(biāo)簽對(duì)比進(jìn)行搜索。
- ASID:全稱為 Address Space ID,一般用于 多進(jìn)程系統(tǒng),下文會(huì)詳細(xì)講述。
- 描述符:由 2個(gè) 部分組成,分別為 物理地址(一部分bit) 和 內(nèi)存區(qū)域?qū)傩?/strong> 組成??梢岳斫鉃?cache 中的數(shù)據(jù)。
一般情況下,切換 進(jìn)程 時(shí)會(huì)切換 頁(yè)表,因?yàn)殡S著進(jìn)程的切換, 虛擬地址 到 物理地址 的映射已經(jīng)改變。此時(shí)需要 清理TLB(即無(wú)效化TLB中的數(shù)據(jù)) 來(lái)保持 TLB一致性。清理TLB 一般通過(guò) 協(xié)處理器CP15 來(lái)完成,在 Linux內(nèi)核 中,有 flush_tlb_all() 和 flush_tlb_range() 函數(shù)來(lái)完成該工作。
2.3 MMU組成
如下圖所示:

MMU 的工作流程可以總結(jié)為下面 2 種情況:
- 訪問(wèn) 虛擬地址 時(shí),MMU 通過(guò)查找 TLB 來(lái)找出對(duì)應(yīng)的 頁(yè)幀,從而訪問(wèn) 物理地址,如圖中的 頁(yè)1、頁(yè)2 和 頁(yè)3。
- 如果 MMU 在 TLB 中沒(méi)找到對(duì)應(yīng)的 TLB行 時(shí),將進(jìn)行 traslation table working。即從 物理地址空間 的 頁(yè)表中 找出對(duì)應(yīng)的 頁(yè)表項(xiàng),并根據(jù) 頁(yè)表項(xiàng) 找到對(duì)應(yīng)的 物理地址。并將該 頁(yè)表項(xiàng) 更新到 TLB 中,以備下次使用。
2.4 MMU工作過(guò)程
ARMv7 下的 MMU 具有 2級(jí)頁(yè)表,分為 1級(jí)頁(yè)表 和 2級(jí)頁(yè)表。
2.4.1 1級(jí)頁(yè)表
1級(jí)頁(yè)表 也稱 主頁(yè)表 和 段頁(yè)表,下面簡(jiǎn)稱 L1頁(yè)表。它將 4GB 的地址空間劃分為 4096 個(gè) 1MB 大小的 段,每個(gè)段的地址為 32bit。所以 1級(jí)頁(yè)表 擁有 4096 個(gè) 32bit 的 頁(yè)表項(xiàng)。
2.4.1.1 一級(jí)頁(yè)表項(xiàng)
L1頁(yè)表 使用了 短描述符頁(yè)表(Short-descriptor translation table),其 頁(yè)表項(xiàng) 具有以下特征:
- 32bit 的頁(yè)描述符
- 具有 2級(jí) 以上的 頁(yè)表
- 支持 32bit 的 物理地址
- 支持 4種 內(nèi)存大?。?
- 16MB/1M,稱為 段
- 64KB/4KB,稱為 頁(yè)
在前面說(shuō)了 TTBR寄存器 是存放 頁(yè)表物理地址 的寄存器,需要注意的是:存放在TTBR寄存器的地址需要16KB對(duì)齊
一級(jí)頁(yè)表項(xiàng) 一共有 4種 格式,如下圖所示:

每種格式都由 物理地址部分+屬性部分 組成,可以直接在圖中看出 物理地址部分 的示意,這里不多贅述。各種格式的含義如下:
- 1MB段轉(zhuǎn)換頁(yè)表項(xiàng)(Section) ,映射到 1MB 的物理地址范圍。其 物理地址部分 即為所需要映射的 物理基地址。
- 物理地址部分 指向 2級(jí)頁(yè)表 的 物理基地址。
- 16MB段(SuperSection) 轉(zhuǎn)換頁(yè)表項(xiàng),是一種特殊的 1MB段轉(zhuǎn)換頁(yè)表項(xiàng)。其 物理地址部分 即為所需要映射的 物理基地址。
- 無(wú)效頁(yè)表項(xiàng),當(dāng)訪問(wèn)該頁(yè)表項(xiàng)時(shí),將觸發(fā) 指令取指異常 或 取數(shù)據(jù)異常
下面簡(jiǎn)單說(shuō)下各個(gè)字段的含義:
- Ignored:忽略
- Level 2 Descriptor Base Address:二級(jí)頁(yè)表物理基地址
- Section Base Address:1MB段基地址
- Supersection Base Address:16MB段基地址
- SBZ:全稱 should be zero,無(wú)效屬性字段
- AP:全稱 Access Permissions,內(nèi)存區(qū)域訪問(wèn)權(quán)限
- Domain:用于權(quán)限控制,下文講述。
- TEX:全稱 Type extension,設(shè)置內(nèi)存區(qū)域類型
- B:全稱 Bufferable,是否設(shè)置 寫緩沖。
- C:全稱 Cacheable,是否設(shè)置 cache。
- nG:全稱 non-Global。如果 頁(yè)表項(xiàng)的nGbit 被設(shè)置,那么該 頁(yè)表項(xiàng) 對(duì)應(yīng)的 內(nèi)存區(qū)域 將只能被 特定的進(jìn)程 使用。當(dāng)MMU 使用該 頁(yè)表項(xiàng) 進(jìn)行映射時(shí),也需要使用到 ASID。
- S:全稱 Shareable,共享設(shè)置項(xiàng)。
- bit[18]:該 bit 決定 段頁(yè)表項(xiàng) 是 1MB頁(yè)表項(xiàng) 還是 16MB頁(yè)表項(xiàng)。
-
bit[1:0]:這 2個(gè)bit 決定頁(yè)表項(xiàng)的類型,如下:
- 00:無(wú)效頁(yè)表項(xiàng)
- 01:轉(zhuǎn)換表頁(yè)表項(xiàng)
- 10:段頁(yè)表項(xiàng)
2.4.1.2 一級(jí)頁(yè)轉(zhuǎn)換
以 1MB段 舉例,假設(shè) L1頁(yè)表 的物理地址為 0x12300000,現(xiàn)在有一個(gè)虛擬地址 0x00100000。其轉(zhuǎn)換過(guò)程如圖所示:


- 查表過(guò)程:將 虛擬地址高12bit,即0x001 乘以 4 得到 0x004。0x004 即為 該虛擬地址所在段的頁(yè)表項(xiàng)在頁(yè)表中的偏移,所以 該虛擬地址對(duì)應(yīng)的頁(yè)表項(xiàng)的物理地址為0x12300000+0x004=0x12300004。
- 根據(jù)查到的 頁(yè)表項(xiàng),將 頁(yè)表項(xiàng)高12bit 和 虛擬地址低30bit 結(jié)合,即為 該虛擬地址在該1MB段內(nèi)的物理地址
值得注意的是:例子中,高12位一共是4096個(gè)頁(yè)表項(xiàng),那么4096x4一共是16384字節(jié)的大小,因?yàn)槊總€(gè)頁(yè)表項(xiàng)是32位。所以4096個(gè)頁(yè)表項(xiàng)需要16K大小的內(nèi)存來(lái)存儲(chǔ)頁(yè)表。也是因?yàn)槿绱?,每個(gè)虛擬地址的高12bit都需要乘以4.
下圖為例子的完整轉(zhuǎn)換過(guò)程,其余類型的 頁(yè)表項(xiàng) 轉(zhuǎn)換過(guò)程類似、

2.4.2 二級(jí)頁(yè)表
2級(jí)頁(yè)表 一共有 256 個(gè) 4字節(jié)大小 的 頁(yè)表項(xiàng),總共占據(jù) 1KB大小 的內(nèi)存空間。L2頁(yè)表 的大部分內(nèi)容與 L1頁(yè)表 類似,相同部分下文將不再贅述
2.4.2 二級(jí)頁(yè)表項(xiàng)
二級(jí)頁(yè)表項(xiàng) 一共有 3種 格式,如下圖所示:

每種格式與 L1頁(yè)表項(xiàng) 一樣由 物理地址部分+屬性部分 組成,可以直接在圖中看出 物理地址部分 的示意,格式如下:
2級(jí)頁(yè)表項(xiàng) 具有以下特征:
- 粗頁(yè)表項(xiàng): 其 物理地址部分指向 64KB大小 的 物理基地址。
- 細(xì)頁(yè)表項(xiàng): 其 物理地址部分指向 4KB大小 的 物理基地址
- 無(wú)效頁(yè)表項(xiàng),當(dāng)訪問(wèn)該頁(yè)表項(xiàng)時(shí),將觸發(fā) 指令取指異常 或 取數(shù)據(jù)異常
屬性字段 的含義請(qǐng)參考 1級(jí)頁(yè)表 章節(jié)。
2.4.2 二級(jí)頁(yè)表轉(zhuǎn)換
L2頁(yè)表 的轉(zhuǎn)換過(guò)程與 L1頁(yè)表 的轉(zhuǎn)換過(guò)程一脈相承。以 4KB 為例子,如下圖所示:

由上圖可以看出其轉(zhuǎn)換步驟如下:
- 通過(guò) 虛擬地址 找出 L1頁(yè)表項(xiàng) 并轉(zhuǎn)換為 L2頁(yè)表 的 基地址。
- 根據(jù) L2頁(yè)表基地址 并集合 虛擬地址的[19:12]bit 找出 虛擬地址 對(duì)應(yīng)的 L2頁(yè)表項(xiàng)。
- 將 虛擬地址[11:0]bit 和 L2頁(yè)表項(xiàng) 的 物理地址部分 結(jié)合得出具體的 物理地址
結(jié)合 L1頁(yè)表 的完整轉(zhuǎn)化過(guò)程如下圖所示:

2.5 MMU內(nèi)存屬性
2.5.1 內(nèi)存區(qū)域權(quán)限
每個(gè) 內(nèi)存區(qū)域 都有自己的權(quán)限,不符合訪問(wèn)權(quán)限的 內(nèi)存訪問(wèn) 都會(huì)引發(fā) 異常。如果是 數(shù)據(jù)訪問(wèn) 則引發(fā) 數(shù)據(jù)異常。如果是 指令訪問(wèn),且該指令在執(zhí)行前沒(méi)有被 flush,將引發(fā) 預(yù)取指異常。
引發(fā)的 異常原因 將會(huì)被設(shè)置在 CP15 的 the fault address and fault status registers
內(nèi)存區(qū)域權(quán)限 由 AP、APX 和 Domain(域) 共同控制,如下:
-
AP/APX:字段 AP 和 APX 的不同組合將形成不同的 訪問(wèn)權(quán)限,如圖所示。按照筆者理解,
Privileged 指的是 CPU 處于 svc 等狀態(tài),而 Unprivileged 則為 CPU 處于 user 狀態(tài)。
訪問(wèn)權(quán)限組合表 -
Domain:這是一種 ARM架構(gòu) 不常用的 內(nèi)存全權(quán)限控制 方式。MMU 可以將所有 內(nèi)存區(qū)域 分配到 16個(gè)域 中,每一個(gè) 域 都有自己的 訪問(wèn)權(quán)限。被分配到 域 中的 內(nèi)存區(qū)域 必須遵循該 域 的訪問(wèn)權(quán)限??梢酝ㄟ^(guò)設(shè)置 頁(yè)表項(xiàng) 中的 Domain字段 來(lái)實(shí)現(xiàn) 域 的分配。
在 CP15協(xié)處理器 中有一個(gè) DACR寄存器(Domain Access Control Register),該寄存器為 32bit,每個(gè) 域 的 訪問(wèn)權(quán)限 由 2個(gè)bit 設(shè)置,一共設(shè)置 16個(gè)域。域 的權(quán)限設(shè)置如下:- 不可訪問(wèn)(no-access):對(duì)該 域 的 內(nèi)存區(qū)域 進(jìn)行訪問(wèn)將引發(fā) 異常。
- 管理者(Manager mode):訪問(wèn)不受任何控制,不產(chǎn)生 異常
- 用戶(Client mode):使用 頁(yè)表項(xiàng) 中的 AP/APX字段 進(jìn)行控制
需要注意的是:內(nèi)存區(qū)域控制以域控制為主,頁(yè)表項(xiàng)的AP/APX字段為次。ARMv7不建議使用域進(jìn)行控制,所以建議把DACR寄存器設(shè)置為用戶模式
2.5.2 內(nèi)存類型
ARM架構(gòu) 實(shí)現(xiàn)了 3種內(nèi)存類型,每種類型都是 互斥的,如下:
- Strongly-ordered
- Device
- Normal
每種 類型 的細(xì)節(jié)如下圖所示:

需要注意的是,Device類型的Shareable內(nèi)存區(qū)域現(xiàn)在已經(jīng)被棄用
內(nèi)存區(qū)域類型 可以通過(guò) TEX字段、C字段 和 B字段 來(lái)進(jìn)行設(shè)置,如下圖所示

值得注意的是:按照筆者理解,inner cache是L1 cache,而outer cache是指在L1cache下面的cache,比如L2cache
操作系統(tǒng)如何使用頁(yè)表
2.6 進(jìn)程與MMU
操作系統(tǒng) 會(huì)為 每個(gè)進(jìn)程 分配一個(gè) 頁(yè)表,該 頁(yè)表 使用 物理地址 存儲(chǔ)。當(dāng) 進(jìn)程 使用類似 malloc 等需要 映射代碼或數(shù)據(jù) 的操作時(shí),操作系統(tǒng) 會(huì)在隨后馬上 修改頁(yè)表 以加入新的 物理內(nèi)存。當(dāng)進(jìn)程完成退出時(shí),內(nèi)核會(huì)將相關(guān)的頁(yè)表項(xiàng)刪除掉,以便分配給新的進(jìn)程。
2.6.1 Address Space ID
在操作系統(tǒng)中, 多進(jìn)程 是一種常態(tài)。那么多進(jìn)程 的情況下,每次 切換進(jìn)程 都需要進(jìn)行 TLB清理。這樣會(huì)導(dǎo)致切換的效率變低。
為了解決問(wèn)題,TLB 引入了 ASID(Address Space ID) 。ASID 的范圍是 0-255。
ASID 由操作系統(tǒng)分配,當(dāng)前進(jìn)程的ASID值 被寫在 ASID寄存器(使用CP15 c3訪問(wèn))。TLB 在更新 頁(yè)表項(xiàng) 時(shí)也會(huì)將 ASID 寫入 TLB。
如果設(shè)置了如果 當(dāng)前進(jìn)程的ASID,那么 MMU 在查找 TLB 時(shí), 只會(huì)查找 TLB 中具有 相同ASID值 的 TLB行。且在切換進(jìn)程是,TLB 中被設(shè)置了 ASID 的 TLB行 不會(huì)被清理掉,當(dāng)下次切換回來(lái)的時(shí)候還在。所以ASID 的出現(xiàn)使得切換進(jìn)程時(shí)不需要清理 TLB 中的所有數(shù)據(jù),可以大大減少 切換開銷。
具體可以看參考鏈接《多核MMU和ASID管理邏輯》
2.6.2 TTBR0和TTBR1
前面講了 TTBR寄存器 是用于存放 頁(yè)表基地址,在 ARmv7 中一共有 2個(gè) 這樣的寄存器,分別是 TTBR0 和 TTBR1。
那么這里提出一個(gè)問(wèn)題:在進(jìn)行 Translation Table walking 的時(shí)候,選擇哪個(gè)TTBR寄存器,又如何選擇?
在 ARMv7 中,有一個(gè)寄存器為 TTBCR(TTB Control Register),即TTB控制寄存器。TTBCR寄存器 可以被設(shè)置為 0-7 這幾個(gè)值。
在進(jìn)行 地址映射 時(shí), MMU 會(huì)根據(jù) TTBCR寄存器 中的值查看 虛擬地址 是高位地址,根據(jù) 高位地址 選擇對(duì)應(yīng)的 TTBR寄存器。
舉個(gè)例子,假設(shè) TTBCR寄存器 被設(shè)置為 4,則 MMU 會(huì)檢查 虛擬地址 的 高4bit,如果 高4bit 都為 0,則此時(shí)選擇 TTBR0。
需要注意的是:TTBCR被設(shè)置為 0 時(shí),默認(rèn)選擇 TTBR0。
下面我們看看使用和不使用 TTBR1 帶來(lái)的影響。
- 不使用:在 ARM32架構(gòu)的操作系統(tǒng)中,不使用 TTBR1寄存器。此時(shí),用戶空間 和 內(nèi)核空間 共用一個(gè) 頁(yè)表。也就是說(shuō) 用戶空間 和 內(nèi)核空間 都使用 TTBR0 來(lái)記錄 頁(yè)表地址,這樣可以避免一個(gè)問(wèn)題,就是進(jìn)行 用戶空間和內(nèi)核空間的切換時(shí),可以避免切換頁(yè)表帶來(lái)的性能損耗。但與此同時(shí)也帶來(lái)一個(gè)問(wèn)題,用戶空間 的 每個(gè)進(jìn)程 都擁有 內(nèi)核頁(yè)表副本,當(dāng) 內(nèi)核空間頁(yè)表 修改時(shí),所有 進(jìn)程 都需要同步修改其 內(nèi)核頁(yè)表副本。造成一定的性能損失
- 使用:ARM64架構(gòu)的操作系統(tǒng)中,虛擬地址空間 非常大。用戶空間 和 內(nèi)核空間 都是 256T。用戶空間地址高位為0,內(nèi)核空間地址高位為1。這樣的特性滿足 TTBR1寄存器 的使用條件。根據(jù) 用戶空間地址 和 內(nèi)核空間地址 的不同,選擇對(duì)應(yīng)的 TTBR寄存器。這樣就不需要為每個(gè) 進(jìn)程 維護(hù)一份 內(nèi)核頁(yè)表副本。
2.6.3 代碼實(shí)例
本小節(jié)簡(jiǎn)單地講述一下 Linux 進(jìn)行 MMU切換 時(shí)的代碼片段。以 ARMv7單核CPU 為例子。
根據(jù)筆者的理解,其調(diào)用圖譜如下:
->switch_mm
->check_and_switch_context
->cpu_switch_mm(processor.switch_mm)
->cpu_v7_switch_mm
筆者會(huì)將簡(jiǎn)單的說(shuō)明注釋在代碼中,不進(jìn)行另外的說(shuō)明。
/* arch/arm/include/asm/mmu_context.h */
static inline void
switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
#ifdef CONFIG_MMU
unsigned int cpu = smp_processor_id();
/*
* __sync_icache_dcache doesn't broadcast the I-cache invalidation,
* so check for possible thread migration and invalidate the I-cache
* if we're new to this CPU.
*/
/* 這里應(yīng)該是說(shuō)進(jìn)程如果調(diào)度到新的CPU,則需要將該CPU的cache給清理掉 */
if (cache_ops_need_broadcast() &&
!cpumask_empty(mm_cpumask(next)) &&
!cpumask_test_cpu(cpu, mm_cpumask(next)))
__flush_icache_all();
if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next) {
/* 如果調(diào)度的進(jìn)程不是本進(jìn)程,則執(zhí)行check_and_switch_context */
check_and_switch_context(next, tsk);
if (cache_is_vivt())
cpumask_clear_cpu(cpu, mm_cpumask(prev));
}
#endif
}
static inline void check_and_switch_context(struct mm_struct *mm,
struct task_struct *tsk)
{
if (unlikely(mm->context.vmalloc_seq != init_mm.context.vmalloc_seq))
__check_vmalloc_seq(mm);
if (irqs_disabled())
/*
* cpu_switch_mm() needs to flush the VIVT caches. To avoid
* high interrupt latencies, defer the call and continue
* running with the old mm. Since we only support UP systems
* on non-ASID CPUs, the old mm will remain valid until the
* finish_arch_post_lock_switch() call.
*/
mm->context.switch_pending = 1;
else
/* 使用該函數(shù)進(jìn)行MMU切換頁(yè)表 */
cpu_switch_mm(mm->pgd, mm);
}
/* arch/arm/include/asm/proc-fns.h */
/* 根據(jù)筆者找的代碼,cpu_switch_mm 應(yīng)該直接調(diào)用了processor.switch_mm */
#define cpu_do_switch_mm processor.switch_mm
#define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)
processor.switch_mm 是一個(gè) 回調(diào)函數(shù),根據(jù)筆者找到的資料,應(yīng)該是指向 ** arch/arm/mm** 目錄下的一些列 MMU 操作代碼。這里以 proc-v7-2level.S(即ARMv7 2級(jí)頁(yè)表) 進(jìn)行說(shuō)明
/* arch/arm/mm/proc-v7-2level.S */
/* 根據(jù)APCS,傳入的參數(shù)是存放在寄存器 r0和r1 */
ENTRY(cpu_v7_switch_mm)
#ifdef CONFIG_MMU
mmid r1, r1 @ get mm->context.id
ALT_SMP(orr r0, r0, #TTB_FLAGS_SMP)
ALT_UP(orr r0, r0, #TTB_FLAGS_UP)
#ifdef CONFIG_PID_IN_CONTEXTIDR
mrc p15, 0, r2, c13, c0, 1 @ read current context ID
lsr r2, r2, #8 @ extract the PID
bfi r1, r2, #8, #24 @ insert into new context ID
#endif
#ifdef CONFIG_ARM_ERRATA_754322
dsb
#endif
mcr p15, 0, r1, c13, c0, 1 @ set context ID
isb
/* 在這里,將r0所指向的頁(yè)表基地址設(shè)置到TTBR0中,完成頁(yè)表的切換 */
mcr p15, 0, r0, c2, c0, 0 @ set TTB 0
isb
#endif
bx lr
ENDPROC(cpu_v7_switch_mm)
三、參考鏈接
《ARM Cortex-A Series Programmer’s Guide》
《Cortex-A7 MPCore Technical Reference Manual》
《多核MMU和ASID管理邏輯》
TLB的作用及工作過(guò)程
MMU和cache詳解(TLB機(jī)制)
inux-kernel – Linux內(nèi)核ARM轉(zhuǎn)換表庫(kù)(TTB0和TTB1)
ARM TTBR0,TTBR1寄存器與ARM32頁(yè)表復(fù)制
選擇使用TTBR0或TTBR1做為translation table base地址寄存器
TLB中ASID和nG bit的關(guān)系
ASID
Linux arm 進(jìn)程切換
ARM-LINUX的進(jìn)程切換
