每个异常级别,即EL3、EL2或EL1,都有自己的虚拟地址空间。应用程序都有独立的虚拟内存空间,作为应用程序无需了解系统的硬件内存布局。为了能够使得操作系统同时运行多个任务,现代CPU都设计了MMU(内存管理单元),其作用是管理虚拟地址和物理地址的映射关系。应用层的虚拟地址看起来连续,实际映射的时候,物理地址很大可能是碎片化的。


虚拟地址:我们编写应用程序所使用到的运行地址;

物理地址:应用程序在硬件层面使用的地址。


MMU会使用虚拟地址中比较重要的位来索引映射表中的条目,以确定哪个块会被访问,从而将虚拟地址转换为物理地址,这种转换是硬件自动完成的。

除了完成地址转换外,MMU还会控制内存访问权限、内存排序和每个区域内存的缓存策略。

下面的图展示了虚拟地址到物理地址的转换过程,MMU会根据映射表来查找虚拟地址对应的物理页表入口,对于用户态的内存映射表,我们使用的映射表TTBR0_EL0,而对于内核态,对应的TTBR1_EL1。区别就是TTBR1_EL1可以映射外设物理地址、ROM和RAM,而TTBR0_EL0只能映射到RAM。ARMv8架构通过这种方式来分离映射用户层和内核层的虚拟地址。


基础篇--MMU和TLB_虚拟地址

TLB(D5.9)

TLB全名是转址旁路缓存,是一种缓存映射表条目的技术。


基础篇--MMU和TLB_ARM_02

为什么要设计TLB?

在没有TLB之前,虚拟地址的映射需要从内存中取映射表,这就导致了频繁的内存访问,反而会降低系统映射性能。TLB的设计初衷就是支持将映射表存入缓存区域,大大提高映射效率。在虚拟地址映射时,MMU会检查映射关系是否已经被缓存在了TLB中,如果已被缓存,那么就会引起缓存命中,此映射关系立即可用。

如果TLB中不包含我们需要的映射关系怎么办?

如果TLB不包含处理器发出的虚拟地址的有效映射,称为TLB缺失,则执行外部映射表查表。MMU内的专用硬件使其能够读取内存中的映射表。然后,新加载的映射可以被缓存在TLB中,以便在映射表查表没有导致页面故障的情况下进行重复使用。

TLB中都包含了啥?

每一个TLB条目不仅物理地址和虚拟地址,还包含诸如内存类型、缓存策略、访问权限、地址空间ID(ASID)和虚拟机ID(VMID)等属性。

TLB可以容纳固定数量的条目。我们可以通过最大限度地减少由映射表遍历引起的外部内存访问数量并获得高TLB命中率来实现最佳性能。ARMv8-A架构提供了一个称为连续块条目的功能,可以有效地使用TLB空间。映射表块条目每个都包含一个连续的位。当设置时,该位向TLB发出信号,它可以缓存一个涵盖多个块的映射的单一条目。一个查找可以索引到一个连续块所覆盖的地址范围的任何地方。因此,TLB可以为定义的地址范围缓存一个条目,使得在TLB中存储更大范围的虚拟地址成为可能。

**要使用一个连续的位,连续的块必须是相邻的,也就是说它们必须对应于一个连续的虚拟地址范围。它们必须从一个对齐的边界开始,具有一致的属性,并指向同一级别转换的连续输出地址范围。**要求的对齐方式是,4KB颗粒的VA[20:16]或64KB颗粒的VA[28:21],对所有地址都是一样的。块条目需要以下数量:

  • 16x4KB相邻块
  • 32x32MB相邻块
  • 32x64KB相邻块

基础篇--MMU和TLB_ARMv8_03

映射颗粒大小

映射控制寄存器TCR_EL1控制了EL1和EL0的内存管理功能。寄存器如下图所示,中级物理地址大小(IPS)字段控制最大输出地址大小。如果映射指定了超出此范围的输出地址,则访问失败,000=32位物理地址,101=48位。两位映射颗粒(TG)TG1和TG0字段分别给出内核或用户空间的颗粒大小,00=4KB,01=16KB,11=64KB


基础篇--MMU和TLB_ARMv8_04

完整的映射过程可能需要三到四个级别的映射表。你不需要实现所有级别。第一个级别的查找实际上是由颗粒大小和TCR_ELn.TxSZ字段决定的。

颗粒大小对映射表的影响

AArch64支持映射三种不同的颗粒大小。这些颗粒定义了映射表最低层的块大小,并控制使用中的映射表的大小。较大的颗粒大小减少了所需的页表层数,这在使用管理程序提供虚拟化的系统中可能成为一个重要因素。

支持的颗粒大小为4KB、16KB和64KB,支持这三种颗粒的大小是由具体设计方案决定的。创建页表的代码能够读取系统寄存ID_AA64MMFR0_EL1,以了解哪些是支持的大小。Cortex-A53处理器支持所有三种尺寸,但一些早期版本的处理器不是这样的,比如Cortex-A57,它不支持16K颗粒尺寸。在映射控制寄存器(TCR_EL1)中,每个映射表的大小都可以配置。

如果VA输入范围限制为42位,则可以省略一级映射表。根据可能的VA范围的大小,级数可能会更少。例如,对于4KB颗粒,如果TTBCR设置为低地址仅跨度1GB,则不需要级别0和1,映射从2级开始,4KB页面从3级开始。

  • 4KB
    当你使用4kB的颗粒大小时,硬件可以使用4级查找过程。48位地址每一级有9个地址位被翻译,即每一级有512个条目,最后的12位在4kB内选择一个字节,直接来自原始地址。
    虚拟地址的第47:39位索引到512个条目的L0表。每个表项都跨越512GB范围,并指向一个L1表。在这个512条目的L1表中,第38:30位被用作索引来选择一个条目,每个条目都指向一个1GB块或一个L2表。位29:21索引到一个512条目的L2表,每个条目指向一个2MB块或下一级表。在最后一级,第20:12位索引到一个512条目的L2表,每个条目指向一个4kB的块。
    基础篇--MMU和TLB_ARM_05
  • 16KB
    当你使用16KB的颗粒大小时,硬件可以使用4级查找过程。48位的地址每一级映射有11个地址位,即每一级有2048个条目,最后的14位在4KB内选择一个字节,直接来自原始地址。0级表只包含两个条目。虚拟地址的第47位从两个条目的L0表中选择一个描述符。这些表项中的每一个都跨越了128TB的范围,并指向一个L1表。在这个2048个条目的L1表内,第46:36位被用作索引来选择一个条目,每个条目指向一个L2表。第35:25位索引到一个2048个条目的L2表,每个条目指向一个32MB的块或下一个表层。在最后的映射阶段,Bit[24:14]索引到一个2048个条目的L2表,每个条目指向一个16KB的块。
    基础篇--MMU和TLB_ARM_06
  • 64KB
    当你使用64kB的颗粒大小时,硬件可以使用3级查找过程。第1级表只包含64个条目。
    虚拟地址的第47:42位从64个条目的L1表中选择一个描述符。这些表项中的每一个都跨越了4TB的范围,并指向一个L2表。在这个8192个条目的L2表内,第41:29位被用作索引来选择一个条目,每个条目指向一个512MB的块或一个L2表。在最后的DeepL,第28:16位索引到一个8192条目的L3表,每个条目都指向一个64kB的块。
    基础篇--MMU和TLB_虚拟地址_07