news 2026/9/12 17:32:30

AArch64 Linux 虚拟内存布局深度解析:从页表级数、TTBR 切换到 52-bit VA 支持

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AArch64 Linux 虚拟内存布局深度解析:从页表级数、TTBR 切换到 52-bit VA 支持

AArch64 Linux 虚拟内存布局深度解析:从页表级数、TTBR 切换到 52-bit VA 支持

【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux

本指南基于 Linux 内核仓库中的 Documentation/arch/arm64/memory.rst,系统讲解 AArch64 Linux 内核的虚拟内存布局设计:包括页大小(4KB/16KB/64KB)与翻译表级数如何决定虚拟地址空间大小、用户态与内核态如何通过 TTBR0/TTBR1 区分、固定内核映射区域的排布规律,以及 ARMv8.2-LVA 带来的 52-bit 虚拟地址支持及其对内核二进制、KASAN shadow 和用户态地址分配的影响。读完本文,你将掌握 VA_BITS/VA_BITS_MIN/vabits_actual 三组变量的区别与用途,能够理解 mmap 高位 hint 触发 52-bit 用户地址的机制,并能在阅读内核源码时快速定位内存布局相关的宏与实现。

AArch64 虚拟地址空间的宏观设计

AArch64 架构本身支持最多 4 级翻译表(translation tables)配合 4KB 页大小使用,或最多 3 级翻译表配合 64KB 页大小使用。Linux 内核在此基础上,通过"页大小 + VA 位数"的组合来确定实际使用的翻译表级数:

页大小翻译表级数虚拟地址位数虚拟地址空间大小
4KB3 级39-bit512GB
4KB4 级48-bit256TB
64KB2 级42-bit4TB

其中 64KB 页配置下,42-bit 虚拟地址空间的内存布局与其他配置保持一致。ARMv8.2 还引入了可选的 Large Virtual Address space(LVA)特性,只有在 64KB 页大小下才可用,它通过扩展第一级翻译表中的描述符数量来扩大地址空间。

这些组合关系在 arch/arm64/Kconfig 中有完整的体现:ARM64_4K_PAGESARM64_16K_PAGESARM64_64K_PAGES三个选项决定页大小;ARM64_VA_BITS_36(仅 16KB 页 + EXPERT)、ARM64_VA_BITS_39(仅 4KB 页)、ARM64_VA_BITS_42(仅 64KB 页)、ARM64_VA_BITS_47(仅 16KB 页)、ARM64_VA_BITS_48ARM64_VA_BITS_52则决定虚拟地址位数,最终汇总为编译期常量CONFIG_ARM64_VA_BITS

TTBRx 选择与内核/用户空间分离

AArch64 Linux 通过虚拟地址的第 55 位(bit 55)来决定使用哪个翻译表基址寄存器(TTBRx):

  • swapper_pg_dir仅包含内核(全局,global)映射,其地址写入TTBR1,且永远不会写入 TTBR0;
  • 用户 pgd 仅包含用户(非全局,non-global)映射。

也就是说,内核虚拟地址空间位于高半区(bit 55 置 1,走 TTBR1),用户空间位于低半区(bit 55 为 0,走 TTBR0)。这也是 arch/arm64/include/asm/memory.h 中PAGE_OFFSET被定义为-(UL(1) << VA_BITS)(即_PAGE_OFFSET(VA_BITS))的原因:线性映射(linear map)起始于 TTBR1 地址空间的开头,PAGE_END则是线性映射的结束、其他内核映射的开始。

#define _PAGE_OFFSET(va) (-(UL(1) << (va))) #define PAGE_OFFSET (_PAGE_OFFSET(VA_BITS)) #define _PAGE_END(va) (-(UL(1) << ((va) - 1)))

内核源码中普遍使用vabits_actual来判断当前实际生效的 VA 位数。在 arch/arm64/include/asm/memory.h 中可以看到它的两种形态:

#if VA_BITS > 48 #define vabits_actual (64 - ((read_tcr() >> 16) & 63)) #else #define vabits_actual ((u64)VA_BITS) #endif

当编译配置的VA_BITS > 48时,vabits_actual在运行时从TCR_EL1.T1SZ字段(位 [21:16])实时读取,从而反映硬件是否真的支持大地址空间;否则直接退化为编译期常量VA_BITS

内核虚拟地址空间的固定区域排布

虽然 memory.rst 正文未逐一列出地址值,但从 arch/arm64/include/asm/memory.h 的宏定义可以完整还原内核 TTBR1 高半区各区域的排布(由高地址向低地址):

  • FIXADDR_TOP-UL(SZ_8M),固定映射区(fixmap)顶部;
  • PCI_IO_START/PCI_IO_ENDVMEMMAP_END + SZ_8M起,大小为PCI_IO_SIZE(SZ_16M),即 PCI I/O 空间;
  • VMEMMAP_END-UL(SZ_1G),vmemmap 区域的结束;
  • VMEMMAP_STARTVMEMMAP_END - VMEMMAP_SIZE,vmemmap(struct page 数组)区域的开始;
  • MODULES_VADDR_PAGE_END(VA_BITS_MIN),内核模块区域开始;MODULES_END = MODULES_VADDR + MODULES_VSIZE,其中MODULES_VSIZE为 SZ_2G;KIMAGE_VADDR即内核镜像起始地址,等于MODULES_END
  • PAGE_OFFSET:线性映射起始,也是 TTBR1 空间的最低位边界。

vmemmap 区域是"线性区域对应的 struct page 数组",其大小必须足以覆盖整个线性区。在 52-bit 支持场景下,arch/arm64/include/asm/memory.h 给出了关键设计:

#define VMEMMAP_RANGE (_PAGE_END(VA_BITS_MIN) - PAGE_OFFSET) #define VMEMMAP_SIZE ((VMEMMAP_RANGE >> PAGE_SHIFT) * sizeof(struct page))

这里的VMEMMAP_RANGE覆盖了从 52-bit 的PAGE_OFFSET一直到 48-bit 的PAGE_END_PAGE_END(VA_BITS_MIN))的整个区间。其注释明确指出:之所以这样设计,是为了保持PAGE_OFFSET恒定,同时在硬件不支持 52-bit 时"回退"使用 vmemmap 的高端部分。这正是"一个二进制同时支持 48-bit 与 52-bit"的核心机制。

当启用 KASAN(Generic 或 Software Tag-Based 模式)时,内核 VA 空间还会被占用 1/8 或 1/16 作为 shadow 内存。arch/arm64/include/asm/memory.h 中的映射公式为:

shadow_addr = (addr >> KASAN_SHADOW_SCALE_SHIFT) + KASAN_SHADOW_OFFSET

其中KASAN_SHADOW_START依赖vabits_actual动态计算,而PAGE_END会被重定义为KASAN_SHADOW_START,即线性映射的结束点由 KASAN shadow 的起点决定。由于 shadow 只占内核 VA 空间的一小部分,其结束地址在 48-bit 与 52-bit 下保持恒定(只依赖~0UL),而起始地址在切换到 52-bit 时会向更低地址方向"生长",这一点在 memory.rst 中也有明确说明。

在 arch/arm64/mm/mmu.c 的map_mem()函数中,可以验证线性映射的结束点直接使用了_PAGE_END(VA_BITS_MIN)

static const u64 direct_map_end = _PAGE_END(VA_BITS_MIN);

并对线性区与 vmalloc 区是否共享 PGD 表项做了BUILD_BUG_ON编译期断言,保证线性区可以安全地设置分层的 PXNTable 属性。此外,arch/arm64/mm/mmu.c 中的linear_map_split_to_ptes()展示了线性映射的运行时调整:在 KPTI 场景下由 boot CPU 以_PAGE_OFFSET(vabits_actual)为起点把线性映射拆分为 PTE 粒度,同时借助 BBML3 机制与 idmap 配合保证其他 CPU 的安全切换。

KVM 与 Hypervisor 的地址映射

当使用 KVM 但未启用Virtualization Host Extensions(VHE)时,hypervisor 在 EL2 以固定的(且可能是随机的)偏移映射内核页,该偏移相对线性映射计算。相关细节可查看kern_hyp_va宏与kvm_update_va_mask函数。

arch/arm64/kvm/va_layout.c 中的kvm_compute_layout()展示了 HYP VA 的生成格式(V 为 hypervisor VA 位数):

63 ... V | V-1 | V-2 .. tag_lsb | tag_lsb - 1 .. 0 --------------------------------------------------------- | 0000000 | hyp_va_msb | random tag | kern linear VA | |--------- tag_val -----------|----- va_mask ---|

即:HYP VA 的低位保留内核线性 VA,中间插入一个(在CONFIG_RANDOMIZE_BASE开启时)随机化的 tag,高位确保与 idmap 区域不冲突。随后kvm_update_va_mask()(arch/arm64/kvm/va_layout.c)通过 alternatives 机制把这套掩码/移位/加法逻辑现场修补进kern_hyp_va指令序列中;若系统支持 VHE 或 tag 为零,则相关指令被替换为 NOP。在启用 VHE 时,宿主内核直接运行在 EL2,因此不需要创建任何额外的映射

memory.rst 还提到,MMIO 设备(如 GICv2)会被映射到 HYP idmap 页旁边;当 CPU 需要ARM64_SPECTRE_V3A加固时,异常向量(vectors)也会映射到该区域——对应kvm_patch_vector_branch()(arch/arm64/kvm/va_layout.c)中为 Spectre-v3a 场景修补向量跳转指令的逻辑。

52-bit VA:内核侧的支持机制

ARMv8.2-LVA 特性存在且使用 64KB 页时,内核和用户空间都可以使用 52-bit 地址空间。但任何支持 52-bit 的内核二进制必须能在早期启动阶段回退到 48-bit,以兼容不支持该特性的硬件。

这种回退机制带来三个约束:

  1. 内核 .text 必须位于较高地址,使得其虚拟地址在 48-bit 与 52-bit 下保持"不变"(invariant),否则切换位宽会改变内核代码自身的寻址;
  2. KASAN shadow 的结束地址必须位于内核 VA 空间的高半区,因为它是~0UL的函数,与具体位宽无关;切换从 48-bit 到 52-bit 时,只有 shadow 的起始地址向低地址方向增长;
  3. PAGE_OFFSET必须保持恒定0xFFF0000000000000(对应 52-bit),从而免去一次额外的变量读取,优化phys_to_virtvirt_to_phys的开销——物理/虚拟偏移(physvirt offset)和 vmemmap 偏移都在启动早期计算好以支撑该逻辑。

单一二进制同时支持 48-bit 与 52-bit 还要求VMEMMAP 的尺寸必须按 52-bit 的 VA 空间来规划,同时还要能容纳固定的PAGE_OFFSET——这正是上一节VMEMMAP_RANGE覆盖到_PAGE_END(VA_BITS_MIN)的原因。

VA_BITS / VA_BITS_MIN / vabits_actual

内核中绝大多数代码无需关心具体 VA 位数,只有少数代码需要知道 VA 空间大小,此时使用三组不同的量(memory.rst 的原文定义):

名称类型含义
VA_BITS常量编译期配置的最大VA 空间大小
VA_BITS_MIN常量编译期配置的最小VA 空间大小
vabits_actual变量运行时实际生效的 VA 空间大小

最大与最小值的用途在于:确保缓冲区按"最坏情况"(最大 VA)足够大,或确保地址按"最坏情况"(最小 VA)放置得足够近。从 arch/arm64/include/asm/memory.h 可以看到VA_BITS_MIN的取值规则:当VA_BITS > 48时,16KB 页配置取 47,其余取 48;否则VA_BITS_MIN就等于VA_BITS

52-bit 用户空间地址:mmap hint 与强制模式

为了兼容依赖 ARMv8.0 最大 48-bit VA 空间的旧软件,内核默认仍然向用户空间返回 48-bit 范围内的虚拟地址。软件可以"主动选择"接收 52-bit 空间的地址:传入一个大于 48-bit 的 mmap hint 参数即可,例如 memory.rst 给出的示例:

maybe_high_address = mmap(~0UL, size, prot, flags,...);

~0UL作为 hint 意味着"尽量往高地址分配",从而触发 52-bit 地址空间的分配路径。这一行为在 arch/arm64/Kconfig 的ARM64_VA_BITS_52帮助文本中也有印证:开启 52-bit 虚拟寻址后,用户空间仅在通过 hint 显式请求时才使用 52-bit 地址,内核自身也会在硬件支持时使用 52-bit 虚拟地址(否则回退到 48-bit)。

此外,还可以构建一个专门用于调试的内核,强制所有用户空间地址都来自 52-bit 空间,只需启用两个配置项:

CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y

从 arch/arm64/Kconfig 的说明来看,CONFIG_ARM64_FORCE_52BIT会禁用 48-bit 兼容逻辑,在硬件支持时把所有用户空间地址强制为 52-bit。该选项仅用于压力测试用户空间内存管理代码,绝不应用于生产环境。

另外需要注意一个安全权衡:52-bit 虚拟寻址与 ARMv8.3 Pointer Authentication(PAC)组合使用时,PAC 位数会从 7 位缩减到 3 位,显著降低其抗暴力破解能力——Kconfig 对此有明确警告,这也是官方建议"不确定时选择 48-bit"的原因之一。

总结

AArch64 Linux 的虚拟内存布局是一个由页大小、翻译表级数、VA 位数和 KASAN/KVM 等特性共同塑造的复杂系统:TTBR0/TTBR1 按 bit 55 划分用户与内核空间;PAGE_OFFSETMODULES_VADDRVMEMMAPPCI_IOFIXADDR_TOP依次排布于高半区;52-bit 支持则以"保持 PAGE_OFFSET 恒定 + 按 52-bit 规划 vmemmap + 启动期回退"为设计核心,配合VA_BITS/VA_BITS_MIN/vabits_actual三组量实现单二进制双位宽兼容。理解这些机制,是深入阅读 arch/arm64/mm/mmu.c、arch/arm64/include/asm/memory.h 以及 arch/arm64/kvm/va_layout.c 等核心代码的前提,也是排查地址空间类内核问题的基础。

【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 17:31:59

RVC 训练 total_epoch 怎么根据训练集音质与时长选择?

RVC 训练 total_epoch 怎么根据训练集音质与时长选择&#xff1f; 【免费下载链接】Retrieval-based-Voice-Conversion-WebUI Easily train a good VC model with voice data < 10 mins! 项目地址: https://gitcode.com/GitHub_Trending/re/Retrieval-based-Voice-Convers…

作者头像 李华
网站建设 2026/9/12 17:27:44

p5.js 贡献者入门指南:从 Issue 到 Pull Request 的完整协作流程

p5.js 贡献者入门指南&#xff1a;从 Issue 到 Pull Request 的完整协作流程 【免费下载链接】p5.js p5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It is bas…

作者头像 李华
网站建设 2026/9/12 17:24:30

2026年7月深圳市坪山区二手房价格深度分析报告

一、报告背景与数据说明本报告基于2026年7月深圳市坪山区实际成交案例&#xff0c;结合贝壳、中原、乐有家等主流平台公开挂牌与成交数据&#xff0c;对坪山区二手房市场价格走势、板块分化、户型结构与购房建议进行深度分析。数据统计周期为2026年7月1日至7月31日&#xff0c;…

作者头像 李华
网站建设 2026/9/12 17:23:02

Umi-OCR 完整指南:如何在 Linux 上配置离线文字识别

Umi-OCR 完整指南&#xff1a;如何在 Linux 上配置离线文字识别 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片&#xff0c;PDF文档识别&#xff0c;排除水印/页眉页脚&#xff0c;扫描/生成二维码。内置多国语言库…

作者头像 李华
网站建设 2026/9/12 17:22:03

电影入库后别手贴海报了:Radarr 海报墙自动化攻略

电影入库后别手贴海报了&#xff1a;Radarr 海报墙自动化攻略 【免费下载链接】Radarr Movie organizer/manager for usenet and torrent users. 项目地址: https://gitcode.com/GitHub_Trending/ra/Radarr 一部片子下完、改名、入库&#xff0c;海报却还是个灰色占位图…

作者头像 李华