Linux内核页表缓存(TLB)与巨型页

一、页表缓存(TLB)

处理器厂商在内存管理单元 (MMU) 里增加一个 TLB (Translation Lookaside Buffer) 的高速缓存,TLB 直译为转译后备缓冲器,也被翻译为页表缓存。

TLB 为 CPU 的一种缓存,由存储器管理单元用于改进虚拟地址到物理地址的转译速度。

TLB 用于缓存一部分标签页表条目。TLB 可介于 CPU 和 CPU 缓存之间,或在 CPU 缓存和主存之间,这取决于缓存使用的是物理寻址或是虚拟寻址。

1.TLB表项格式

不同处理器架构的 TLB 表项的格式不同。ARM64 处理器的每条 TLB 表项不仅包含虚拟地址和物理地址,也包含属性:内存类型、缓存策略、访问权限、地址空间标识符 (ASID) 及虚拟机标识符 (VMID)。地址空间标识符区分不同进程的页表项,虚拟机标识符区分不同虚拟机的页表项。

2.TLB管理

若内核修改了可能缓存在TLB里面的页表项,那么内核必须负责使旧的TLB表项失效,内核定义了每种处理器架构必须实现的函数。

3.ARM64架构提供一条TLB失效指令

当 TLB 没有命中时,ARM64 处理器的内存管理单元自动遍历内存中的页表,把页表复制到 TLB,不需要软件把页表写到 TLB,所以 ARM64 架构没有提供写 TLB 的指令。

ARM64 架构提供一条 TLB 失效指令

字段<type>常见选项:(ALL:所有表项。VMALL:当前虚拟机的阶段 1 的所有表项,即表项的 VMID 是当前虚拟机的 VMID,虚拟机里面运行客户操作系统的虚拟地址转换成物理地址分成两个阶段,第 1 阶段把虚拟地址转换成中间物理地址,第 2 阶段把中间物理地址转换成物理地址。ASID:匹配寄存器 Xt 指定的 ASID 的表项。VA:匹配寄存器 Xt 指定的虚拟地址和 ASID 的表项。VAA:匹配寄存器 Xt 指定的虚拟地址并且 ASID 可以是任意值的表项。)

字段<level>指定异常级别:(E1:异常级别 1。E2:异常级别 2。E3:异常级别 3。)

字段 {IS} 表示内部共享,即多个核共享;如果不使用字段 IS,表示非共享,只被一个核使用。

字段 Xt 是 X0-X31 中的任一个寄存器。

4.地址空间标识符

为了减少在进程切换时清空页表缓存的需要,ARM64 处理器的页表缓存使用非全局(not global, nG)位区分内核和进程的页表项,使用地址空间标识符(Address Space Identifier,ASID)区分不同进程的页表项。

ARM64 处理器 ASID 长度是由具体实现定义的,可以选择 8 位或者 16 位,寄存器 ID_AA64MMFR0_EL1(AArch64 内存模型特性寄存器 0,AArch64 Memory Model Feature Register 0)的字段 ASIDBits 存放处理器支持的 ASID 长度。

  • 如果系统中进程数量超过 256,则可能会有多个进程需要共享同一个 ASID,这就导致地址空间混淆问题。

为了避免这种问题,内核引入了 ASID 版本号:

  1. 每个进程不仅有一个硬件 ASID(低 8 位),还有一个软件 ASID,包含 ASID 的版本号(高 56 位)。

  2. 系统维护一个全局 ASID 版本号,用于标识当前的 ASID 使用状态。

  3. 在进程调度时,比较进程的 ASID 版本号和全局 ASID 版本号:

    • 如果相同,则复用进程的硬件 ASID。
    • 如果不同,则分配新的硬件 ASID,并将版本号更新为全局 ASID 版本号。
  4. 当硬件 ASID 耗尽时,内核将全局 ASID 版本号加一,同时标记所有现有的 ASID 项目无效(如清空页表缓存)。

  5. 具体例子

    假设系统中有以下情况:

    ASID 的长度为 8 位,因此有 256 个硬件 ASID(编号范围:1-255,0 为保留值)。系统中有 300 个进程正在运行。

    • 进程 A 和 B 首次运行时分配 ASID
      • 假设当前全局 ASID 版本号为 1。
      • 系统给进程 A 分配硬件 ASID 1,并将其软件 ASID 记录为(版本号:1,硬件 ASID:1)。
      • 系统给进程 B 分配硬件 ASID 2,并将其软件 ASID 记录为(版本号:1,硬件 ASID:2)。
    • 进程 C 再次分配 ASID
      • 由于系统中只有 256 个硬件 ASID,假设硬件 ASID 已分配完(1 到 255)。
      • 系统将全局 ASID 版本号加一,变为 2。
      • 清空处理器的页表缓存(TLB),标记所有已有的硬件 ASID 无效。
      • 给进程 C 分配硬件 ASID 1,并将其软件 ASID 记录为(版本号:2,硬件 ASID:1)。
    • 进程切换时的处理
      • 当进程 A 再次被调度时,比较进程 A 的 ASID 版本号(1)和全局 ASID 版本号(2):
        • 不相同,说明 ASID 需要重新分配。
        • 系统给进程 A 分配硬件 ASID 2,并将其软件 ASID 记录为(版本号:2,硬件 ASID:2)。

5.虚拟机标识符

虚拟机里面运行的客户操作系统的虚拟地址换成物理地址分两个阶段:第 1 阶段把虚拟地址转换成中间物理地址,第 2 阶段把中间物理地址转换成物理地址。第 1 阶段转换由客户操作系统的内存控制,和非虚拟化的转换过程相同。第 2 阶段转换由虚拟机监控器控制,虚拟机监控器为每个虚拟机维护一个转换表,分配一个虚拟机标识符(Virtual Machine Identifier,VMID),寄存器 VTTRBR_EL2(虚拟化转换表基准寄存器,Virtualization Translation Table Base Register)存放当前虚拟机的阶段 2 转换表的物理地址。

  • 定义:VMID 是虚拟化环境中使用的标识符,用来区分不同虚拟机的地址空间。在虚拟化环境下,多个虚拟机可共享同一物理 CPU,每个虚拟机有自己的虚拟地址空间,VMID 使 TLB 缓存条目能区分不同虚拟机的虚拟地址空间。
  • 作用:虚拟化系统中,主机操作系统(宿主机)通过 Hypervisor 管理多个虚拟机(客体操作系统)。为在 TLB 中区分不同虚拟机地址空间,需使用 VMID,确保虚拟机虚拟地址映射不与其他虚拟机或宿主机冲突。
  • 举个例子:
    • 假设有 VM1 和 VM2 两个虚拟机,运行在同一物理服务器,各有虚拟地址空间。
    • 设 VM1 的 VMID 为0x1,VM2 的 VMID 为0x2
    • VM1 执行时,TLB 缓存条目关联 VMID 0x1;VM2 切换到 CPU 时,Hypervisor 将 TLB 中 VMID 设为0x2,避免 VM2 的 TLB 条目与 VM1 冲突。

二、巨型页

当运行内存需求量较大的应用程序时,如果使用长度为 4KB 的页,将会产生较多的 TLB 未命中和缺页异常,严重影响应用程序的性能。如果使用长度为 2MB 甚至更大的巨型页,可以大幅减少 TLB 未命中和缺页异常的数量,大幅提高应用程序的性能。这才是内核引入巨型页(Huge Page)的真正原因。

巨型页首先需要处理器能够支持,然后需要内核支持,内核有两种实现方式:

・使用 hugetlbfs 伪文件系统实现巨型页;

・透明巨型页。

hugetlbfs 文件系统是一个假的的文件系统,只是利用了文件系统的编程接口。使用 hugetlbfs 文件系统实现的巨型页称为传统巨型页、或者称标准巨型页。

透明巨型页,标准巨型页的优点是预先分配巨型页到巨型页池,进程申请巨型页的时候从巨型页池取,成功的概率很高,缺点是应用程序需要使用文件系统的编程接口。透明巨型页的优点是对应用程序透明,缺点是动态分配,在内存碎片化的时候分配成功的概率很低。

用户态协议栈使用巨型页的原因

用户态协议栈(如网络协议栈)常处理大规模数据,对内存访问频率高、需求量大。若使用 4KB 小页,会因频繁的地址转换导致大量 TLB 未命中缺页异常,增加内存管理开销,拖慢数据处理速度。而巨型页(如 2MB)通过减少页表条目数量,降低 TLB 未命中概率,减少缺页异常,提升虚拟地址到物理地址的转换效率,进而优化用户态协议栈的内存访问性能,降低延迟,提高网络数据处理、转发等操作的效率。

1.处理器对巨型页的支持

ARM64处理器支持巨型页的方式有两种:

1.通过块描述符支持巨型页

2.通过页/块描述符的连续为支持巨型页。

页 / 块描述符中的连续位指示表项是一个连续表项集合中的一条表项,一个连续表项集合可以被缓存在一条 TLB 表项里面。通常所说的,进程申请了 n 页的虚拟内存区域,然后申请了 n 页的物理内存区域,使用 n 个连续的页表项把每个虚拟页映射到物理页,每页表项设置了连续标志位,当处理器的内存管理单元遍历内存的页表时,访问到 n 个页表项中的任何一个页表项。发现页表项设置了连续标志位,就会把 n 个页表项合并以后填充到 TLB 表项。

假设:页长度为 4KB,那么使用 4 级转换表,1 级转换表的块描述符不能使用连续位;2 级转换表的块描述符支持 16 个连续块,即支持(162MB=32MB)巨型页,3 级转换表的页描述符支持 16 个连续页,即支持(164KB=64KB)巨型页。

假设:如果页长度 16KB,那么我们使用 4 级转换表,2 级转换表的块描述符支持 32 个连续块,即支持(3232MB=1GB)巨型页;3 级转换表的页描述符支持 128 个连续页,即支持(12816KB=2MB)巨型页。

假设:如果页长度 64KB,那么使用 3 级转换表,2 级转换表的块描述符不能使用连续位;3 级转换表的页描述符支持 32 个连续页。即支持(32*64KB=2MB)巨型页。

2.标准巨型页

通过文件 "cat /proc/sys/nr_hugepages" 指定巨型页池中永久巨型页的数量。

通过文件 "cat /proc/sys/vm/nr_overcommit_hugepages" 指定巨型页池中临时巨型页的数量,当永久巨型页用完的时候,可以从页分配器申请临时巨型页。

nr_hugepages 是巨型页池的最小长度,(nr_hugepages+nr_overcommit_hugepages)是巨型页池的最大长度,这两个参数默认值都是 0,至少要设置一个,否则分配巨型页会失败。

3.查看巨型页信息

通过虚拟文件系统查看: cat /proc/meminfo

4.巨型页池

内核使用巨型页池管理巨型页。有的处理器架构支持多种巨型页长度,每种巨型页长度对应一个巨型页池,有一个默认的巨型页长度,默认只创建巨型页长度是默认长度的巨型页池。比如 ARM64 架构在页长度是 4KB 的时候支持巨型页长度是 1GB、32MB、2MB、64KB,默认的巨型页长度是 2MB,默认只有创建巨型页长度是 2MB 的巨型页池。

巨型页池中的巨型页可分为两种:

永久巨型页:是保留的,不能有其他用途,被预先分配到巨型页池,当进程释放永久巨型页的时候,永久巨型页被归还到巨型页池。

临时巨型页:也称为多余的 (surplus) 巨型页,当永久巨型页用完的时候,可以从页分配器分配临时巨型页;进程释放临时巨型页的时候,直接释放到页分配器。当设备长时间运行后,内存可能碎片化,分配临时巨型页可能会失败。

hugetlb 相当于 hugepages 页面管理者,页面的分配及释放,都是由此模块负责。

hugetlbfs 则用于向用户提供一套基于文件系统的巨型页使用界面,下层功能的实现,主要依赖于 hugetlb。

https://github.com/0voice

相关推荐
即将头秃的程序媛17 分钟前
centos 7.9安装tomcat,并实现开机自启
linux·运维·centos
fangeqin26 分钟前
ubuntu源码安装python3.13遇到Could not build the ssl module!解决方法
linux·python·ubuntu·openssl
爱奥尼欧2 小时前
【Linux 系统】基础IO——Linux中对文件的理解
linux·服务器·microsoft
超喜欢下雨天2 小时前
服务器安装 ros2时遇到底层库依赖冲突的问题
linux·运维·服务器·ros2
tan77º3 小时前
【Linux网络编程】网络基础
linux·服务器·网络
笑衬人心。4 小时前
Ubuntu 22.04 + MySQL 8 无密码登录问题与 root 密码重置指南
linux·mysql·ubuntu
chanalbert5 小时前
CentOS系统新手指导手册
linux·运维·centos
星宸追风6 小时前
Ubuntu更换Home目录所在硬盘的过程
linux·运维·ubuntu
热爱生活的猴子6 小时前
Poetry 在 Linux 和 Windows 系统中的安装步骤
linux·运维·windows
myloveasuka6 小时前
[Linux]内核如何对信号进行捕捉
linux·运维·服务器