天天看點

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

目錄

0、回顧

1、TLB

1.1、TLB coherency

2、MMU

2.1、TTBR0、TTBR1、TTBCR

2.1.1、TTBCR

2.1.2、TTBR0、TTBR1

2.2、Translation Table

2.2.1、L1 Address Translation

2.2.2、L2 Address Translation

3、OS Usage Of Translation Tables

3.1、Address Space ID(ASID)

3.2、TTBR0、TTBR1

之前寫過 MMU 的一些入門和基礎的分析《初探 MMU》和《ARMv7-A 的 MMU 淺析》,有基于概念掌握和基本入門的一些了解,這裡打算在針對 ARMv7-A 的處理器再次稍微深入一點研究一下他的 MMU 和 TLB;

這一版同樣基于 ARM 官方文檔:

ARMv7-A_and_R_Architecture_Reference_Manual

DEN0013D_cortex_a_series_PG

的一個是完整版的 ARMv7-A 的處理器架構文檔,第二個是 Cortex-A 系列的 Programmer Guide;

由于有前面的兩篇文章墊底(《初探 MMU》和《ARMv7-A 的 MMU 淺析》),這裡就不再介紹一些基礎的内容了,直接單刀直入;

0、回顧

前面知道,MMU 用作虛拟位址和實體位址的互相轉換,是為了能夠給 OS 提供統一視角的虛拟位址空間;

TLB 的作用是作為 MMU 的 Cache,以提高 MMU 的性能,他們之間的關系如下:

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

1、ARM 處理器發出位址通路(虛拟位址),首先過 MMU 位址翻譯單元的 TLB,如果 TLB 命中,那麼直接傳回真實的實體位址;

2、如果 TLB Miss,那麼就要靠 Table Walk 單元去主存中查找表,以擷取實體位址,然後通過 Cache,去通路;

3、Cache 如果命中,那麼直接傳回實際實體位址的資料,否則,也就是最糟糕的情況,會去通路主存的資料;

上面的過程呢,軟體要做的,隻有配置并放好這個 Transliation Tables,其他的過程,全部是硬體行為;下面馬上仔細的過這部分的細節;

使能 MMU 的參考代碼(因為是 CP15 的系統控制寄存器,是以使用 MRC/MCR 指令):

MRC p15, 0, R1, c1, C0, 0 ;Read control register
ORR R1, #0x1 ;Set M bit
MCR p15, 0,R1,C1, C0,0 ;Write control register and enable MMU
           

這裡要注意的一點是,可能要用到記憶體屏障指令,因為這裡就開啟了 MMU,即将進入虛拟記憶體的世界,要確定在這之前,流水線幹淨,是以執行已經完畢;

1、TLB

TLB 的全稱是:Translation Lookaside Buffer;從第一節的那個圖可以看出來,MMU 做 Table Walk 的這個 Transliation Tables 是放到主存中,主存通路速度很慢(加 Cache 的根本原因),是以,這裡每次都去再主存中做 Table Walk,顯然效率非常低,是以,這裡就為這個 Table Walk 定制了一個屬于他的 “Cache”,稱之為 TLB;

但是與 真是的 Cache 不一樣(詳見《ARMv7-A 處理器窺探(4) —— Cache》),這個 TLB 是專門緩存 Transliation Tables 的,典型的情況,他的組成如下:

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

由 VA、ASID、PA、Attributes 組成,即:

VA:虛拟位址;

PA:實體位址;

ASID:Address Space ID;

Attributes:屬性;

1.1、TLB coherency

TLB 既然扮演的 Transliation Tables Cache 的角色,那麼也會有一緻性問題,最典型的就是再 OS 中,上下文切換的時候,上一個程序的虛拟位址對應的實體位址表,肯定是和另一個不一樣,導緻 TLB 一緻性問題;此刻,OS 必須處理這種情況,使得上一個程序的 TLB 對下一個失效,也可以直接通過 CP15 控制寄存器,來 flush 掉 TLB(代價太大);

2、MMU

這裡,抛開大實體位址擴充和 section 和 supersection 的分析,暫時就看最最常用的兩段查找;兩段頁表查找,我們稱第一級頁表為 L1,第二級為 L2;

2.1、TTBR0、TTBR1、TTBCR

前面知道,軟體需要負責建構這個虛拟位址到實體位址的轉換表:Transliation Tables,當軟體構件完畢這個表後,隻需要告訴硬體,這個 Transliation Tables 放到了那個首位址即可,這個配置通過寫 ARM 的 TTBR 寄存器實作(Translation Table Base Address );這裡其實有兩個 TTRB 寄存器,分别叫 TTBR0 和 TTBR1,為啥兩個,後面解釋;

2.1.1、TTBCR

和這個 TTBR0、TTBR1 勾肩搭背的,還有一個 TTBCR 寄存器,他們直接什麼關系呢,看寄存器說明:

TTBCR:

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

PD0 和 PD1 是和 Security Extensions 相關的,不管他;

EAE 是和 Large Physical Address Extension  相關的,不管他;

主要關注這裡的 N[2:0],訓示TTBR0頁表基址寄存器基址位寬,同時訓示使用 TTBR0 還是 TTBR1 作為頁表基址寄存器,以及 TTBR0 頁表尺寸:

如果 N = 0 的話,則在做 Table Walk 的時候使用 TTBR0 指定的基位址作為 Transliation Tables 入口的位址;

如果 N > 0 的話:訓示TTBR0頁表基址寄存器基址位寬,同時訓示使用 TTBR0 還是 TTBR1 作為頁表基址寄存器,以及 TTBR0 頁表尺寸;

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables
  • N==0,使用 TTBR0。
  • N>0,如果虛拟位址[31:32-N]為0,則使用 TTBR0;其他情況使用TTRB1。這種情況下,N 訓示了TTBR1的頁表位址,也訓示了 TTBR0 的頁表大小。
  • TTRB0的頁表大小由TTBCR.N控制,TTRB1的頁表大小為16KB。

我換句話來說,當 N>0 的時候,比如 N=1,那麼按照這種說法,VA [31:31] 也就是 VA 的 bit[31] 為 0 的時候,使用 TTBR0 否則使用 TTRB1,按照位址空間來劃分,即,32bits 位址,當最高位為 0,即虛拟位址為 0x0000_0000 ~ 0x7FFF_FFFF  這個區間的時候,使用 TTBR0 作為 Transliation Tables 入口的位址,從 0x8000_000 ~ 0xFFFF_FFFF 的虛拟位址空間,使用 TTBR1;

ARM 官方舉了個例子,當 TTBCR.N=3‘b111 的時候,VA [31:25] 全部為 0 的時候,使用 TTBR0,按照位址空間來劃分就是,虛拟位址為:0x0000_0000 ~ 0x01FF_FFFF 這段區間使用 TTBR0 作為 Transliation Tables 入口的位址;

0x0200_0000 ~ 0xFFFF_FFFF 的虛拟位址空間,使用 TTBR1;

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

OK,現在可以了解為,配置 TTBCR.N 這個值,可以實作将虛拟位址切割成為兩部分,一部分使用 TTBR0 指定的 Transliation Tables 進行 Table Walk,另一部分使用 TTBR1 指定的 Transliation Tables 進行 Table Walk,這個有什麼好處呢?比如,核心的頁表,是不會改變的,而程序上下文的頁表是會改變的,有了這個的話,就可以考慮用一個 TTBR 來專門為核心服務,另一個 TTBR 為程序服務,這樣避免程序和核心使用同一個頁表,每次都要進行核心頁表的拷貝;

由于 TTBCR 是 CP15 的寄存器,通路 TTBCR 的指令為:

MRC p15, 0, <Rt>, c2, c0, 2 ; Read TTBCR into Rt
MCR p15, 0, <Rt>, c2, c0, 2 ; Write RT to TTBCR
           

2.1.2、TTBR0、TTBR1

上面說了 TTBCR,下面來看 TTRB0、TTRB1 寄存器描述:

TTBR0

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

在帶有多核處理器擴充的情況下 TTBR0 由一個可變的長度構成 Transliation Tables Base Address,這個 x 就是上面的 (14 - (TTBCR.N));

Bits[31:x]:x=(14-(TTBCR.N))。一級頁表位址;

Bits[x-1:7]:Reserved;

NOS:Not Outer Shareable bit,訓示了做 Table walk 的那個記憶體的屬性,是 Outer Shareable 還是 Inner Shareable.

  • 0  Outer Shareable.
  • 1  Inner Shareable.

TTBR0.S == 0 時,該bit無效;

S:Shareable bit. 訓示記憶體共享屬性與頁表轉換的關系;

  • 0  Non-shareable.
  • 1  Shareable.

RNG:Region bits,訓示 Outer Cache 屬性與頁表轉換的關系;

  • 0b00 Normal memory, Outer Non-cacheable.
  • 0b01 Normal memory, Outer Write-Back Write-Allocate Cacheable.
  • 0b10 Normal memory, Outer Write-Through Cacheable.
  • 0b11 Normal memory, Outer Write-Back no Write-Allocate Cacheable.

IRGN[6,0]:Inner region bits,訓示 Inner Cache 屬性與頁表轉換的關系;

  • 0b00 Normal memory, Inner Non-cacheable.
  • 0b01 Normal memory, Inner Write-Back Write-Allocate Cacheable.
  • 0b10 Normal memory, Inner Write-Through Cacheable.
  • 0b11 Normal memory, Inner Write-Back no Write-Allocate Cacheable.

通路 TTBR0 的指令為:

MRC p15, 0, <Rt>, c2, c0, 0 ; Read 32-bit TTBR0 into Rt
MCR p15, 0, <Rt>, c2, c0, 0 ; Write Rt to 32-bit TTBR0
           

TTBR1

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

它的位域和 TTBR0 幾乎一樣,唯一不一樣的地方在于,配置的位址區間在于 bit[31:14],這意味着,配置進 TTBR1 的 Transliation Tables Base Address 的實體位址,必須 16KB 對齊;

通路 TTBR1 的指令為:

MRC p15, 0, <Rt>, c2, c0, 1 ; Read 32-bit TTBR1 into Rt
MCR p15, 0, <Rt>, c2, c0, 1 ; Write Rt to 32-bit TTBR1
           

2.2、Translation Table

現在我們知道了合理的配置 TTBCR/TTBR0/TTBR1 可以配置設定并指定 Transliation Tables,而這個 Transliation Tables 位于記憶體中,用作 MMU 來做 Table Walk;那麼接下來我們需要知道頁表的結構,這樣我們才能夠在記憶體中建立頁表,并将頁表配置給 TTBR 寄存器,完成 MMU 的配置;

不考慮大位址擴充和 SuperSection 以及 Section 的情況下,針對 Transliation Tables,ARMv7-A 的手冊給出了如下的圖解

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

圖中,我們暫時隻考慮 Page Table 的情況,即紅色部分(其餘的可以照着推);

藍色的部分,可以了解為之前寄存器裡面配置的那個 N 值;這裡為了說明情況,我們暫時将 N 定為 0;

2.2.1、L1 Address Translation

我們先暫時不管使用 TTBR0 還是 TTBR1,其實過程是一樣的;此刻當 N = 0 的時候,一級頁表以虛拟位址(後面簡稱 VA,即 Virtual Address)VA[31:20] 作為 L1 Index,一共 12bits,最大能夠表征 4K 的 Index:

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

每個入口是 4 Bytes 也就是一個 Word,32bits 的入口,L1 Index 從 0~4095,一共 4K,在記憶體上,每個入口 4Bytes,那麼 L1 頁表占用記憶體 4K x 4 Bytes = 16KB;

每一個入口是什麼樣子的呢,我們放大來看:

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

可以看到,這個入口,根據不同的配置,内容有所差別,一共有 4 種類型,這 4 種類型,通過 32bits 的尾部 2 bits 來區分,即,綠色部分(Section 和 SuperSection 的區分,還靠 bit[18]);

這裡我們暫時不關心 Section 和 SuperSection,關注于紅色部分和那個 Fault;

Level 2 Descriptor Base Address:指向的是 L2 頁表的實體位址的基位址;可以看到他是 bit[31:10],是 1KB 邊界對齊的;

這個 Domain 指的是 ARM 支援将記憶體标記為最多 16 個 domain,并以 Domain ID 作為區分,每個 Domain 可以支援配置成為不同的通路權限(通過配置 CP15 的 C3 的 Domain Access Control Register (DACR) 寄存器):

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

配置指令為:

MRC p15, 0, <Rt>, c3, c0, 0 ; Read DACR into Rt
MCR p15, 0, <Rt>, c3, c0, 0 ; Write Rt to DACR
           

針對這個 DACR 寄存器,ARM 官方建議配置成為 Client;

The use of domains is deprecated in the ARMv7 architecture, and will eventually be removed,

but in order for access permissions to be enforced, it is still necessary to assign a domain number

to a section and to ensure that the permission bits for that domain are set to client. Typically, you

would set all domain ID fields to 0 and set all fields in the DACR to ‘Client’.

2.2.2、L2 Address Translation

介紹完 L1 Address Translation 後,下面是二級頁表!與 L1 頁表不一樣,二級頁表不和 N 值挂鈎,它直接采用 VA[19:12]  作為 L2 Index 索引,一共 8 bits,最大能夠表征 256 的 L2 Index;

加入 L2 頁表後結合 L1,通過一個給定的 VA 進行索引的第二步為(圖中表示的 N 值為 0):

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

這樣,一個 VA 通過高位址部分[31:20] 索引到了 L1,再從 L1 指向的 L2 加上 VA[19:12] 作為 L2 Index,索引到 L2 表的固定位置;

L2 也是每條由 4 Bytes 構成,即一個 32bits 的數,那麼一個 L2 表大小為 256 x 4 Bytes = 1024 Bytes = 1KB;一共有 4096 個這樣的 L2,那麼 L2 表總的大小為:4096 x 1KB = 4MB;

我們放大每一條 L2 的入口:

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

我們隻關心紅色部分!可以看到,這個 Small Page Base Address 有 bit[31:12] 也就是 4KB 邊界對齊!接下來我們看剩餘幾個位的含義:

AP/APX:Access Permission 即通路權限,每個記憶體區域 都有自己的權限,不符合通路權限的 記憶體通路都會引發 異常。如果是 資料通路 則引發 資料異常。如果是 指令通路,且該指令在執行前沒有被 flush,将引發 預取指異常。引發的 異常原因将會被設定在 CP15 的 the fault address and fault status registers;

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

記憶體區域類型 可以通過 TEX字段、C字段 和 B字段 來進行設定:

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

XN:指的是 Execute Never,不允許執行,如果往這裡取位址執行,那麼會導緻異常發生;通常,Device memory 類型的區域會配置成為 XN;

S:指的是是否具有 Shareable 屬性;

nG:non-Global,這個标記告訴 MMU,這個頁表是否是一個全局的,什麼意思呢?看下面:

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

當 nG 為 0 的時候,說明此區域是全局可見的,換句話來說,就是任何時候都生效;

當 nG 為 1 的時候,說明此區域不是全局的,要聯合這個 ASID 來确認;

每一個 nG=1 的區域,都會和 ASID 來關聯,ASID (Address Space Identifier),這代表,TLB 可以存在多個不同程序的頁表緩存,後面說 ASID 的時候會仔細說;

自此,L1/L2 分析完畢,那麼整個 Table Walk 的流程為:

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

VA 的 4K 頁内偏移,直接對應到 PA 的 4KB 頁内偏移;

3、OS Usage Of Translation Tables

通常情況下,在使用 Cortex-A 系列處理器的時候,典型場景是跑多任務 OS;每一個任務(或者成為應用),都有它獨立的虛拟位址空間,以及他的獨立的 Translation Table;但是對于 OS 來說,Kernel 的 Translation Tables 其實是固定的,隻是程序之間的 Translation Tables 不一樣而已;

當一個程序啟動的時候,OS 負責為他 code 和 data 段建立映射表(Translation Tables);當程序調用諸如 malloc 之類配置設定記憶體的行為,OS 負責修改 Translation Tables(Linux 中,實際通路配置設定的記憶體的時候,才去修改頁表),程序生命周期消亡,OS 負責回收它的資源和頁表,并可以為下一個新的程序提供資源;每一個程序都有自己的獨立的頁表,這便可以保證程序之間不會互相幹擾;

3.1、Address Space ID(ASID)

在作業系統中,多程序是一種常态。那麼多程序 的情況下,每次切換程序都需要進行 TLB 清理。這樣會導緻切換的效率變低。為了解決問題,TLB 引入了 ASID(Address Space ID) 。ASID 的範圍是 0-255。ASID 由作業系統配置設定,目前程序的ASID值 被寫在 ASID 寄存器 (使用CP15 c3通路)。TLB 在更新頁表項時也會将 ASID 寫入 TLB。

如果設定了如果 目前程序的ASID,那麼 MMU 在查找 TLB 時, 隻會查找 TLB 中具有 相同ASID值 的 TLB行。且在切換程序是,TLB 中被設定了 ASID 的 TLB行 不會被清理掉,當下次切換回來的時候還在。是以ASID 的出現使得切換程序時不需要清理 TLB 中的所有資料,可以大大減少切換開銷。

有了這個 ASID + nG 的機制,那麼 TLB 中就可以緩存不同程序的頁表,不用每次都去 flush TLB,導緻性能的損失:

ARMv7-A 處理器窺探(5) —— MMU/TLB0、回顧1、TLB2、MMU3、OS Usage Of Translation Tables

3.2、TTBR0、TTBR1

前面我們說了 TTBR0、TTBR1 是根據 TTBCR.N 來進行劃分的,典型場景下 OS 跑多任務,如果處理器隻能夠支援一個 TTBR 的話,也就意味着使用者空間和核心空間使用同一個 TTBR,由于核心空間的 code 和 data 幾乎是不變的,但是多任務的使用者空間都是不一樣的,這樣就會存在兩個問題:

1、多個任務的頁表裡面,都有同樣一部分核心映射的拷貝副本;

2、要修改核心映射的時候,所有任務的頁表都要修改;

加入兩個 TTBR 的原因,是因為希望核心和使用者空間使用兩套 TTBR,這樣就可以避免上面的尴尬;核心空間固定使用一組,使用者空間不斷的切換(比如,配合 TTBR0 + ASID 進行性能的提升)

參考文獻:

https://www.jianshu.com/p/ef1e93e9d65b

https://www.cs.rutgers.edu/~pxk/416/notes/10-paging.html

https://blog.csdn.net/liyuewuwunaile/article/details/106773630?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242

繼續閱讀