ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ARM64 4KB内核+64KB大页:混合页表实验全解析

ARM64 4KB内核+64KB大页:混合页表实验全解析 “4KB内核托起64KB进程”这个标题听起来更像是一个噱头但它实际是 ARM64 平台上一个可以动手跑通的页表实验内核地址空间继续用 4KB 小页精细管理用户态进程的关键映射改用 64KB 大页最终在同一个系统里同时存在 4KB 和 64KB 两种粒度的页表映射形成混合形态的“双页表”效果。这个实验的价值不只是“炫技”。ARM64 支持 4KB、16KB、64KB 三种基础页大小大页能显著提升 TLB 覆盖面积、降低页表遍历层级但代价是物理内存浪费更容易出现。4KB 内核搭配 64KB 用户大页理论上可以同时拿到小页的内存利用率和 大页的 TLB 收益这是本实验最值得验证的问题。文章会先讲清楚 ARM64 地址翻译与页大小的基础然后用 QEMU 搭建一个 4KB 页大小的 ARM64 虚拟机再通过 hugetlbfs 让进程实际映射 64KB 大页并用/proc下的信息验证映射是否真的生效最后给出常见问题和工程化边界。适合对 ARM64 内核、Linux 内存管理、嵌入式开发和虚拟化方向感兴趣的读者尤其适合想自己动手编译内核、研究页表机制的人。1. 实验方案速览这个实验不是开箱即用的一键项目而是一套需要自己搭建 QEMU、编译内核、配置大页、编写测试程序的完整内核实验。先看整体规格项目说明实验类型ARM64 内核内存管理 / 页表实验核心目标验证“4KB 内核 64KB 用户进程映射”的混合页表效果推荐环境QEMU 虚拟机无真实 ARM 硬件也可完成内核基础页大小4KB对应CONFIG_ARM64_4K_PAGESy用户态大页大小64KB通过 HugeTLB / hugetlbfs 映射依赖工具QEMU、aarch64 交叉编译工具链、Linux 内核源码、busybox 或发行版 rootfs启动方式qemu-system-aarch64命令行启动API 与批量任务不涉及验证工具/proc/meminfo、/proc/pid/smaps、perf、dmesg适合人群ARM64 内核学习者、Linux 内存管理研究者、嵌入式与虚拟化开发者难易程度中等偏上需要会编译内核、了解基本页表概念注意一点题目里的“64KB 进程”不是说进程本身占 64KB 内存而是说进程的地址空间里至少有一部分映射使用了 64KB 粒度的大页条目。理解这一点后面所有实验步骤就不会跑偏。2. 技术背景ARM64 页大小与地址翻译2.1 ARM64 内核的三种页大小Linux 在 ARM64 平台上编译内核时页面大小是一个全局配置不是运行时选项。内核源码里通过一个choice来三选一CONFIG_ARM64_4K_PAGESy4KB 页经典配置内存利用率高页表四级。CONFIG_ARM64_16K_PAGESy16KB 页折中方案在部分服务端场景比较流行。CONFIG_ARM64_64K_PAGESy64KB 页页表级数少、TLB 覆盖面积大但内存浪费风险高。这里说的“级数”指的是虚拟地址到物理地址翻译时需要经过的页表层级。以 48 位虚拟地址空间为例4KB 页通常需要 4 级页表PGD - PUD - PMD - PTE而 64KB 页只需要 3 级。级数越少一次地址翻译需要访问内存的次数就越少TLB miss 带来的代价也越低。2.2 TLB 覆盖面积决定了性能上限TLB 是 CPU 内部缓存地址翻译结果的硬件结构条目数量非常有限。一个 TLB 条目能覆盖的内存范围越大程序在连续访问大块数据时的 TLB miss 就越少。4KB 页面下一个 TLB 条目只覆盖 4KB 地址范围。换成 64KB 页面一个 TLB 条目可以覆盖 64KB覆盖面积扩大了 16 倍。对于数据库、AI 推理、图像处理这类顺序访问大块内存的负载减少 TLB miss 带来的收益通常非常可观。2.3 混合粒度“双页表”的现实形态很多人以为“双页表”需要两套完全独立的页表这其实是误解。在一个 4KB 页内核里要让用户进程的映射变成 64KB 粒度并不需要重新发明一套页表而是依赖 ARMv8 架构提供的 contiguous 机制内核仍然是 4KB PTE。当 16 个连续的 4KB PTE 满足对齐和连续性条件时硬件允许它们被标记为 contiguous。TLB 可以把这 16 个条目合并成 1 个 64KB 条目等价于一次覆盖 64KB 地址范围。所以“4KB 内核 64KB 用户大页”本质上是“小页管理内核地址空间 大页承载用户态映射”的混合方案。本文后面要做的验证就是把这个机制跑起来并确认它真的生效。3. 为什么要做这个实验3.1 内核保留 4KB 页的理由内核线性映射要覆盖几乎全部物理内存页回收、权限控制、内存热插拔、直接映射的精细管理都希望粒度尽量细。如果整个内核都用 64KB 页一个只读 4KB 的模块数据也可能占一个 64KB 页内部碎片会明显放大。4KB 小页的好处是内存密度高、碎片损失小尤其适合嵌入式设备这样内存紧张的场景。坏处是页表层级多、TLB 压力大但这在大多数负载下可以接受。3.2 用户进程换 64KB 页的收益用户态很多负载是“大块连续数据 顺序访问”视频编解码、矩阵计算、大规模缓存、日志写入。这类负载访问局部性集中在连续区域使用 64KB 大页能明显减少页表项数量和 TLB miss。大页的代价是分配粒度过大。如果一个进程只有几 KB 的热数据硬塞进 64KB 页会浪费 60KB 内存。所以正确做法是普通小对象继续走 4KB 小页关键大数据段显式映射为 64KB 大页。3.3 本实验要验证的关键问题这个实验最终要回答三个问题4KB 页内核上用户进程是否真的能建立 64KB 映射。通过什么方式可以确认映射已经生效而不是停留在“配置了但没效果”。这类混合粒度方案在资源占用和性能观察上需要注意什么。只有把这三个问题跑通才算真正理解 ARM64 页表机制。4. 环境准备与前置条件4.1 宿主机依赖建议使用 Ubuntu/Debian 类 Linux 系统作为宿主机。需要安装以下工具sudo apt update sudo apt install qemu-system-arm qemu-utils \ gcc-aarch64-linux-gnu \ bc flex bison libssl-dev libncurses-dev \ cpiogcc-aarch64-linux-gnu是 ARM64 交叉编译工具链负责编译 Linux 内核和测试程序。qemu-system-arm包里包含qemu-system-aarch64用于启动虚拟机。4.2 交叉编译环境变量编译内核前建议先统一设置环境变量export ARCHarm64 export CROSS_COMPILEaarch64-linux-gnu-这两个变量可以写进~/.bashrc方便后续多次编译。4.3 获取内核源码这里不要用太老的版本建议选择当前还在维护的 Linux 内核源码例如 Linux 6.x 发行分支。cd ~ # 从你熟悉的镜像渠道下载 Linux 内核源码并解压 tar xf linux-*.tar.xz cd linux-*如果你的网络环境访问内核代码仓库方便也可以直接 clone 一个稳定的内核仓库。4.4 准备 rootfsQEMU 启动 ARM64 内核后需要一个根文件系统。最省事的方式是直接使用现成的 ARM64 发行版镜像比如 Ubuntu Cloud 镜像的 ARM64 版本写入一块虚拟磁盘也可以自己用 busybox 构建最小文件系统。用发行版镜像的优点是省时间缺点是启动后环境比较重。用 busybox 构建最小 rootfs 更符合“内核实验”的轻量需求但需要额外制作时间。第一次做实验优先推荐先用发行版镜像跑通流程再考虑精简。5. 启动一个 4KB 页大小的 ARM64 内核5.1 内核配置要点进入内核源码目录后先生成默认配置make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- defconfig然后打开图形配置界面make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- menuconfig需要确认以下配置处于开启状态CONFIG_ARM64_4K_PAGESy CONFIG_HUGETLBFSy CONFIG_HUGETLB_PAGEy CONFIG_TRANSPARENT_HUGEPAGEy其中CONFIG_ARM64_4K_PAGES决定内核基本页大小是 4KBCONFIG_HUGETLBFS和CONFIG_HUGETLB_PAGE是使用 HugeTLB 大页映射的前提CONFIG_TRANSPARENT_HUGEPAGE用于透明大页本实验主要用显式 HugeTLB但开启透明大页方便做对比。ARM64 的 contiguous 页表合并机制通常默认开启不需要手工配置只要不主动关闭它即可。5.2 编译内核配置完成后执行make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- Image -j$(nproc)编译完成后内核镜像位于arch/arm64/boot/Image。如果编译过程中报缺少依赖按缺失的包名安装即可常见的是flex、bison、libssl-dev缺失。5.3 启动 QEMU 虚拟机准备一张 rootfs 虚拟磁盘后用以下命令启动qemu-system-aarch64 \ -machine virt \ -cpu cortex-a72 \ -m 2048 \ -kernel arch/arm64/boot/Image \ -drive filerootfs.img,formatraw,ifvirtio \ -append root/dev/vda1 rw consolettyAMA0 \ -nographic启动参数里的root/dev/vda1需要根据你实际 rootfs 分区位置调整。consolettyAMA0保证内核日志能输出到当前终端。进入系统后先确认基础页大小getconf PAGE_SIZE如果输出4096说明当前内核确实运行在 4KB 页面模式下。5.4 检查大页支持在虚拟机里查看当前内核支持哪些大页尺寸ls /sys/kernel/mm/hugepages/以 4KB 基础页内核为例可能看到hugepages-64kB、hugepages-2MB、hugepages-1GB等目录具体哪些出现取决于内核配置。如果看不到hugepages-64kB说明当前内核没有注册 64KB 大页优先检查CONFIG_HUGETLBFS和CONFIG_HUGETLB_PAGE是否开启也可能是内核版本对这个尺寸支持不同换 2MB 大页做对照实验同样能验证混合粒度原理。6. 功能测试让进程实际映射 64KB 大页6.1 挂载 hugetlbfs先创建挂载点并挂载 hugetlbfs指定页面大小为 64KBsudo mkdir -p /mnt/huge sudo mount -t hugetlbfs hugetlbfs /mnt/huge -o pagesize64K挂载成功后/mnt/huge下写入的文件会直接落在大页内存上。如果这里报Invalid argument说明内核不支持pagesize64K挂载参数可以先用dmesg查看具体报错或者改用 2MB 大页继续实验。6.2 预分配大页在虚拟机里手动预留 16 个 64KB 大页echo 16 | sudo tee /sys/kernel/mm/hugepages/hugepages-64kB/nr_hugepages如果 sysfs 目录名和实际不一致先回到ls /sys/kernel/mm/hugepages/查看准确目录名再写。也可以在内核启动参数里预分配hugepagesz64K hugepages16但启动参数方式对内核支持情况更敏感不是每个内核都会在 4KB 基础页上接受hugepagesz64K建议先使用 sysfs 方式。6.3 编写 64KB 大页映射测试程序在 rootfs
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进