一,、前言 在 嵌入式Linux 開發(fā)中,,往往會聽到 MMU 這個詞,但大多數(shù)情況下并不會去了解它,,因為操作系統(tǒng)已經(jīng)做好了關(guān)于 MMU 的一切操作,,我們只需要在操作系統(tǒng)的框架下直接使用即可。但了解 MMU 有助于幫助我們理解操作系統(tǒng),,理解進程等,,讓我們對 嵌入式Linux 的理解上升一個層次。本文將簡單地講述一下關(guān)于 MMU 的基本信息,。 注意:本文將按照ARMv7的二級頁表映射進行講述 二,、MMU 2.1 MMU基本信息 MMU 全稱為 Memory Management Unit,即 內(nèi)存管理單元,。在 帶有MMU的嵌入式Linux 中,,CPU 訪問的地址都是 虛擬地址,而 MMU 負責將程序中 代碼或數(shù)據(jù) 的 虛擬地址 翻譯為 物理地址,,以便程序訪問內(nèi)存,。 在執(zhí)行操作時,MMU 會自動轉(zhuǎn)換 CPU發(fā)出的虛擬地址,,無法人工進行操作,,只需要配置好 MMU 相關(guān)屬性即可。 虛擬地址 是在 編譯和鏈接 時定義的,,可以簡單地理解為 由鏈接器和鏈接器腳本 指定虛擬地址,。 除了 翻譯虛擬地址,MMU 還可以配置 內(nèi)存區(qū)域 的各項配置,,如內(nèi)存區(qū)域的訪問權(quán)限,,內(nèi)存區(qū)域是否使能cache等功能,。 總結(jié) MMU 的功能,,如下: 翻譯虛擬地址 配置內(nèi)存區(qū)域的相關(guān)屬性 2.2 MMU基本概念 看到 MMU 的相關(guān)文章時,,總會提及幾個概念如 頁,頁框(頁幀),,頁表,,頁表項,TLB等等,,下面我們逐個拆分來講述,。 2.2.1 頁 MMU 管理 虛擬地址空間 時,是按照 頁 為單位來進行管理,。在 ARMv7 的 MMU ,,頁大小 一共有 16M(Super Section)、1M(Section) ,、64K(Large Page) 4K(Page),。頁大小 可以通過 協(xié)處理器CP15 進行配置,越小的頁意味著內(nèi)存的顆粒度越小,,內(nèi)存使用時的浪費會越小,,但也意味著使用的TLB行越多。越大的也內(nèi)存的顆粒度月大,,內(nèi)存的使用浪費也可能月大,,但使用的TLB行越少。比如只需要申請 7K 大小的 物理內(nèi)存,,如果使用 7K大小 的內(nèi)存,,我們可以分配 2 個 4K頁,如果分配 64K的大頁,,則浪費的空間就比較大,。 2.2.2 頁框 因為 虛擬地址空間 需要有所對應(yīng)的 物理地址,這樣才能在 虛擬地址 中存儲數(shù)據(jù),。所以 MMU 管理 物理地址空間 時,,按照 頁幀 為單位進行管理。其大小分為 64K 和 4K,。一段 虛擬地址空間 有可能存在著多個 頁,,這些 頁 對應(yīng)著多個 頁幀。 按照筆者理解,,頁 和 頁幀 是 不同地址空間下的關(guān)于內(nèi)存空間大小的概念,。 2.2.3 頁表及頁表項 MMU 在進行 地址轉(zhuǎn)換 時,需要一些信息,,存放這些信息的就是 頁表,。每個 頁表 的最小單位就是 頁表項。 頁表 存儲在 物理地址空間 中,,且一個 頁表項 對應(yīng)著一個 頁,。 在 切換頁表 時,,通過將 頁表的物理首地址 設(shè)置到 協(xié)處理器CP15 中的 TTBR寄存器(Translation Table Base Register)。此后 MMU 會通過該地址自動去 物理地址空間 中找到對應(yīng)的 頁表,,從而完成 虛擬地址到物理地址的映射,。 在不考慮 TLB 和 多級頁表 的情況下,可以簡單地如下圖所示: 頁表及頁表項 2.2.4 TLB TLB 全程為 Translation Lookaside Buffer,,即 旁路轉(zhuǎn)換緩沖,。它是 MMU 的專屬 全相聯(lián)cache,用于臨時存放 虛擬地址到物理地址映射 所需要的信息,。 下面按照步驟說明 TLB 的作用: CPU 訪問 虛擬地址 到 MMU,。 MMU 根據(jù)規(guī)則(規(guī)則在下文講述)查看 虛擬地址 是否在 TLB 中。 如果在 TLB 中,,則稱為 TLB命中,。從 TLB 中直接獲取 物理地址 對內(nèi)存進行訪問 如果不在 TLB 中,則稱為 TLB失效,。此時 MMU 將進行 translation table walking,,即通過 訪問頁表來獲取 物理地址。并將該 虛擬地址 的信息存入 TLB,,以便下次使用,。 值得注意的是:ARM架構(gòu)的TLB只存儲有效的頁表項,對于無效的頁表項TLB并不會存儲 TLB 由許多 TLB行 組成,,如下圖所示: TLB TLB行 由 3個 部分組成,,分別為 標簽、ASID 和 描述符 標簽:該部分由 虛擬地址的一部分bit 組成,,MMU 通過將 虛擬地址的一部分bit 和 TLB 的所有標簽對比進行搜索,。 ASID:全稱為 Address Space ID,一般用于 多進程系統(tǒng),,下文會詳細講述,。 描述符:由 2個 部分組成,分別為 物理地址(一部分bit) 和 內(nèi)存區(qū)域?qū)傩?組成,??梢岳斫鉃?cache 中的數(shù)據(jù)。 一般情況下,,切換 進程 時會切換 頁表,,因為隨著進程的切換, 虛擬地址 到 物理地址 的映射已經(jīng)改變,。此時需要 清理TLB(即無效化TLB中的數(shù)據(jù)) 來保持 TLB一致性,。清理TLB 一般通過 協(xié)處理器CP15 來完成,在 Linux內(nèi)核 中,,有 flush_tlb_all() 和 flush_tlb_range() 函數(shù)來完成該工作,。 2.3 MMU組成 如下圖所示: MMU組成 MMU 的工作流程可以總結(jié)為下面 2 種情況: 訪問 虛擬地址 時,,MMU 通過查找 TLB 來找出對應(yīng)的 頁幀,從而訪問 物理地址,,如圖中的 頁1、頁2 和 頁3,。 如果 MMU 在 TLB 中沒找到對應(yīng)的 TLB行 時,,將進行 traslation table working。即從 物理地址空間 的 頁表中 找出對應(yīng)的 頁表項,,并根據(jù) 頁表項 找到對應(yīng)的 物理地址,。并將該 頁表項 更新到 TLB 中,以備下次使用,。 2.4 MMU工作過程 ARMv7 下的 MMU 具有 2級頁表,,分為 1級頁表 和 2級頁表。 2.4.1 1級頁表 1級頁表 也稱 主頁表 和 段頁表,,下面簡稱 L1頁表,。它將 4GB 的地址空間劃分為 4096 個 1MB 大小的 段,每個段的地址為 32bit,。所以 1級頁表 擁有 4096 個 32bit 的 頁表項,。 2.4.1.1 一級頁表項 L1頁表 使用了 短描述符頁表(Short-descriptor translation table),其 頁表項 具有以下特征: 32bit 的頁描述符 具有 2級 以上的 頁表 支持 32bit 的 物理地址 支持 4種 內(nèi)存大?。?/p> 16MB/1M,,稱為 段 64KB/4KB,稱為 頁 在前面說了 TTBR寄存器 是存放 頁表物理地址 的寄存器,,需要注意的是:存放在TTBR寄存器的地址需要16KB對齊 一級頁表項 一共有 4種 格式,,如下圖所示: 一級頁表項格式 每種格式都由 物理地址部分+屬性部分 組成,可以直接在圖中看出 物理地址部分 的示意,,這里不多贅述,。各種格式的含義如下: 1MB段轉(zhuǎn)換頁表項(Section) ,映射到 1MB 的物理地址范圍,。其 物理地址部分 即為所需要映射的 物理基地址,。 物理地址部分 指向 2級頁表 的 物理基地址。 16MB段(SuperSection) 轉(zhuǎn)換頁表項,,是一種特殊的 1MB段轉(zhuǎn)換頁表項,。其 物理地址部分 即為所需要映射的 物理基地址。 無效頁表項,,當訪問該頁表項時,,將觸發(fā) 指令取指異常 或 取數(shù)據(jù)異常 下面簡單說下各個字段的含義: Ignored:忽略 Level 2 Descriptor Base Address:二級頁表物理基地址 Section Base Address:1MB段基地址 Supersection Base Address:16MB段基地址 SBZ:全稱 should be zero,無效屬性字段 AP:全稱 Access Permissions,,內(nèi)存區(qū)域訪問權(quán)限 Domain:用于權(quán)限控制,,下文講述,。 TEX:全稱 Type extension,設(shè)置內(nèi)存區(qū)域類型 B:全稱 Bufferable,,是否設(shè)置 寫緩沖,。 C:全稱 Cacheable,是否設(shè)置 cache,。 nG:全稱 non-Global,。如果 頁表項的nGbit 被設(shè)置,那么該 頁表項 對應(yīng)的 內(nèi)存區(qū)域 將只能被 特定的進程 使用,。當MMU 使用該 頁表項 進行映射時,,也需要使用到 ASID。 S:全稱 Shareable,,共享設(shè)置項,。 bit[18]:該 bit 決定 段頁表項 是 1MB頁表項 還是 16MB頁表項。 bit[1:0]:這 2個bit 決定頁表項的類型,,如下: 00:無效頁表項 01:轉(zhuǎn)換表頁表項 10:段頁表項 2.4.1.2 一級頁轉(zhuǎn)換 以 1MB段 舉例,,假設(shè) L1頁表 的物理地址為 0x12300000,現(xiàn)在有一個虛擬地址 0x00100000,。其轉(zhuǎn)換過程如圖所示: 查表過程 轉(zhuǎn)換過程 查表過程:將 虛擬地址高12bit,,即0x001 乘以 4 得到 0x004。0x004 即為 該虛擬地址所在段的頁表項在頁表中的偏移,,所以 該虛擬地址對應(yīng)的頁表項的物理地址為0x12300000+0x004=0x12300004,。 根據(jù)查到的 頁表項,將 頁表項高12bit 和 虛擬地址低30bit 結(jié)合,,即為 該虛擬地址在該1MB段內(nèi)的物理地址 值得注意的是:例子中,,高12位一共是4096個頁表項,那么4096x4一共是16384字節(jié)的大小,,因為每個頁表項是32位,。所以4096個頁表項需要16K大小的內(nèi)存來存儲頁表。也是因為如此,,每個虛擬地址的高12bit都需要乘以4. 下圖為例子的完整轉(zhuǎn)換過程,,其余類型的 頁表項 轉(zhuǎn)換過程類似、 L1轉(zhuǎn)換完整過程 2.4.2 二級頁表 2級頁表 一共有 256 個 4字節(jié)大小 的 頁表項,,總共占據(jù) 1KB大小 的內(nèi)存空間,。L2頁表 的大部分內(nèi)容與 L1頁表 類似,相同部分下文將不再贅述 2.4.2 二級頁表項 二級頁表項 一共有 3種 格式,,如下圖所示: 二級頁表項 每種格式與 L1頁表項 一樣由 物理地址部分+屬性部分 組成,,可以直接在圖中看出 物理地址部分 的示意,格式如下: 2級頁表項 具有以下特征: 粗頁表項: 其 物理地址部分指向 64KB大小 的 物理基地址。 細頁表項: 其 物理地址部分指向 4KB大小 的 物理基地址 無效頁表項,,當訪問該頁表項時,,將觸發(fā) 指令取指異常 或 取數(shù)據(jù)異常 屬性字段 的含義請參考 1級頁表 章節(jié)。 2.4.2 二級頁表轉(zhuǎn)換 L2頁表 的轉(zhuǎn)換過程與 L1頁表 的轉(zhuǎn)換過程一脈相承,。以 4KB 為例子,,如下圖所示: image.png 由上圖可以看出其轉(zhuǎn)換步驟如下: 通過 虛擬地址 找出 L1頁表項 并轉(zhuǎn)換為 L2頁表 的 基地址。 根據(jù) L2頁表基地址 并集合 虛擬地址的[19:12]bit 找出 虛擬地址 對應(yīng)的 L2頁表項,。 將 虛擬地址[11:0]bit 和 L2頁表項 的 物理地址部分 結(jié)合得出具體的 物理地址 結(jié)合 L1頁表 的完整轉(zhuǎn)化過程如下圖所示: image.png 2.5 MMU內(nèi)存屬性 2.5.1 內(nèi)存區(qū)域權(quán)限 每個 內(nèi)存區(qū)域 都有自己的權(quán)限,,不符合訪問權(quán)限的 內(nèi)存訪問 都會引發(fā) 異常。如果是 數(shù)據(jù)訪問 則引發(fā) 數(shù)據(jù)異常,。如果是 指令訪問,,且該指令在執(zhí)行前沒有被 flush,,將引發(fā) 預(yù)取指異常,。 引發(fā)的 異常原因 將會被設(shè)置在 CP15 的 the fault address and fault status registers 內(nèi)存區(qū)域權(quán)限 由 AP、APX 和 Domain(域) 共同控制,,如下: AP/APX:字段 AP 和 APX 的不同組合將形成不同的 訪問權(quán)限,,如圖所示。按照筆者理解,, Privileged 指的是 CPU 處于 svc 等狀態(tài),,而 Unprivileged 則為 CPU 處于 user 狀態(tài)。 訪問權(quán)限組合表 Domain:這是一種 ARM架構(gòu) 不常用的 內(nèi)存全權(quán)限控制 方式,。MMU 可以將所有 內(nèi)存區(qū)域 分配到 16個域 中,,每一個 域 都有自己的 訪問權(quán)限。被分配到 域 中的 內(nèi)存區(qū)域 必須遵循該 域 的訪問權(quán)限,??梢酝ㄟ^設(shè)置 頁表項 中的 Domain字段 來實現(xiàn) 域 的分配。 在 CP15協(xié)處理器 中有一個 DACR寄存器(Domain Access Control Register),,該寄存器為 32bit,,每個 域 的 訪問權(quán)限 由 2個bit 設(shè)置,一共設(shè)置 16個域,。域 的權(quán)限設(shè)置如下: 不可訪問(no-access):對該 域 的 內(nèi)存區(qū)域 進行訪問將引發(fā) 異常,。 管理者(Manager mode):訪問不受任何控制,不產(chǎn)生 異常 用戶(Client mode):使用 頁表項 中的 AP/APX字段 進行控制 需要注意的是:內(nèi)存區(qū)域控制以域控制為主,,頁表項的AP/APX字段為次,。ARMv7不建議使用域進行控制,所以建議把DACR寄存器設(shè)置為用戶模式 2.5.2 內(nèi)存類型 ARM架構(gòu) 實現(xiàn)了 3種內(nèi)存類型,,每種類型都是 互斥的,,如下: Strongly-ordered Device Normal 每種 類型 的細節(jié)如下圖所示: image.png 需要注意的是,Device類型的Shareable內(nèi)存區(qū)域現(xiàn)在已經(jīng)被棄用 內(nèi)存區(qū)域類型 可以通過 TEX字段、C字段 和 B字段 來進行設(shè)置,,如下圖所示 image.png 值得注意的是:按照筆者理解,,inner cache是L1 cache,而outer cache是指在L1cache下面的cache,,比如L2cache 操作系統(tǒng)如何使用頁表 2.6 進程與MMU 操作系統(tǒng) 會為 每個進程 分配一個 頁表,,該 頁表 使用 物理地址 存儲。當 進程 使用類似 malloc 等需要 映射代碼或數(shù)據(jù) 的操作時,,操作系統(tǒng) 會在隨后馬上 修改頁表 以加入新的 物理內(nèi)存,。當進程完成退出時,內(nèi)核會將相關(guān)的頁表項刪除掉,,以便分配給新的進程,。 2.6.1 Address Space ID 在操作系統(tǒng)中, 多進程 是一種常態(tài),。那么多進程 的情況下,,每次 切換進程 都需要進行 TLB清理。這樣會導(dǎo)致切換的效率變低,。 為了解決問題,,TLB 引入了 ASID(Address Space ID) 。ASID 的范圍是 0-255,。 ASID 由操作系統(tǒng)分配,,當前進程的ASID值 被寫在 ASID寄存器(使用CP15 c3訪問)。TLB 在更新 頁表項 時也會將 ASID 寫入 TLB,。 如果設(shè)置了如果 當前進程的ASID,,那么 MMU 在查找 TLB 時, 只會查找 TLB 中具有 相同ASID值 的 TLB行,。且在切換進程是,,TLB 中被設(shè)置了 ASID 的 TLB行 不會被清理掉,當下次切換回來的時候還在,。所以ASID 的出現(xiàn)使得切換進程時不需要清理 TLB 中的所有數(shù)據(jù),,可以大大減少 切換開銷。 具體可以看參考鏈接《多核MMU和ASID管理邏輯》 2.6.2 TTBR0和TTBR1 前面講了 TTBR寄存器 是用于存放 頁表基地址,,在 ARmv7 中一共有 2個 這樣的寄存器,,分別是 TTBR0 和 TTBR1。 那么這里提出一個問題:在進行 Translation Table walking 的時候,,選擇哪個TTBR寄存器,,又如何選擇? 在 ARMv7 中,,有一個寄存器為 TTBCR(TTB Control Register),,即TTB控制寄存器,。TTBCR寄存器 可以被設(shè)置為 0-7 這幾個值。 在進行 地址映射 時,, MMU 會根據(jù) TTBCR寄存器 中的值查看 虛擬地址 是高位地址,,根據(jù) 高位地址 選擇對應(yīng)的 TTBR寄存器。 舉個例子,,假設(shè) TTBCR寄存器 被設(shè)置為 4,,則 MMU 會檢查 虛擬地址 的 高4bit,如果 高4bit 都為 0,,則此時選擇 TTBR0,。 需要注意的是:TTBCR被設(shè)置為 0 時,默認選擇 TTBR0,。 下面我們看看使用和不使用 TTBR1 帶來的影響,。 不使用:在 ARM32架構(gòu)的操作系統(tǒng)中,不使用 TTBR1寄存器,。此時,,用戶空間 和 內(nèi)核空間 共用一個 頁表。也就是說 用戶空間 和 內(nèi)核空間 都使用 TTBR0 來記錄 頁表地址,,這樣可以避免一個問題,,就是進行 用戶空間和內(nèi)核空間的切換時,,可以避免切換頁表帶來的性能損耗,。但與此同時也帶來一個問題,用戶空間 的 每個進程 都擁有 內(nèi)核頁表副本,,當 內(nèi)核空間頁表 修改時,,所有 進程 都需要同步修改其 內(nèi)核頁表副本。造成一定的性能損失 使用:ARM64架構(gòu)的操作系統(tǒng)中,,虛擬地址空間 非常大,。用戶空間 和 內(nèi)核空間 都是 256T。用戶空間地址高位為0,,內(nèi)核空間地址高位為1,。這樣的特性滿足 TTBR1寄存器 的使用條件。根據(jù) 用戶空間地址 和 內(nèi)核空間地址 的不同,,選擇對應(yīng)的 TTBR寄存器,。這樣就不需要為每個 進程 維護一份 內(nèi)核頁表副本。 2.6.3 代碼實例 本小節(jié)簡單地講述一下 Linux 進行 MMU切換 時的代碼片段,。以 ARMv7單核CPU 為例子,。 根據(jù)筆者的理解,其調(diào)用圖譜如下: ->switch_mm ->check_and_switch_context ->cpu_switch_mm(processor.switch_mm) ->cpu_v7_switch_mm 筆者會將簡單的說明注釋在代碼中,,不進行另外的說明,。 /* arch/arm/include/asm/mmu_context.h */static inline voidswitch_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)該是說進程如果調(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)度的進程不是本進程,則執(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ù)進行MMU切換頁表 */ 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 是一個 回調(diào)函數(shù),,根據(jù)筆者找到的資料,應(yīng)該是指向 ** arch/arm/mm** 目錄下的一些列 MMU 操作代碼,。這里以 proc-v7-2level.S(即ARMv7 2級頁表) 進行說明 /* 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所指向的頁表基地址設(shè)置到TTBR0中,,完成頁表的切換 */ mcr p15, 0, r0, c2, c0, 0 @ set TTB 0 isb#endif bx lrENDPROC(cpu_v7_switch_mm) 三,、參考鏈接 《ARM Cortex-A Series Programmer’s Guide》 《Cortex-A7 MPCore Technical Reference Manual》 《多核MMU和ASID管理邏輯》 TLB的作用及工作過程 MMU和cache詳解(TLB機制) inux-kernel – Linux內(nèi)核ARM轉(zhuǎn)換表庫(TTB0和TTB1) ARM TTBR0,TTBR1寄存器與ARM32頁表復(fù)制 選擇使用TTBR0或TTBR1做為translation table base地址寄存器 TLB中ASID和nG bit的關(guān)系 ASID Linux arm 進程切換 ARM-LINUX的進程切換 |
|