挑战 Linus 的“禁区”:从 2026 LSFMM+BPF 大会看每 CPU 页表的性能逆袭 引言在 Linux 内核的高性能网络与内存管理优化中this_cpu操作一直扮演着核心角色。然而这项旨在消除锁竞争的设计在不同的 CPU 架构上却有着截然不同的命运。在2026 年 Linux 存储、文件系统、内存管理和 BPF 峰会LSFMMBPF Summit的内存管理MM讨论会期间内核开发者 Yang Shi 提出了一项针对this_cpu工作原理的根本性修改方案。该方案试图通过引入“每 CPU 页表per-CPU page tables”来抹平架构间的性能鸿沟。然而这一提议不仅颠覆了内核的传统认知更直接触碰了 Linus Torvalds 曾极力反对的底层“禁区”。1. 什么是 per-CPU 变量如何靠它实现“无锁化”在多核SMP系统中当多个 CPU 核心同时读写同一个全局变量时就会引发数据竞争Data Race。传统的保护方式是使用自旋锁Spinlock或互斥锁Mutex。传统锁机制的代价缓存颠簸与排队当 CPU A 加锁修改数据时CPU B 只能自旋等待。更糟糕的是锁机制会导致硬件层面的缓存一致性风暴Cache Invalidation——数据在不同 CPU 的 Cache Line 之间频繁搬移即缓存颠簸/Cache Thrashing这会带来巨大的性能损耗。per-CPU 变量的魔法空间换时间为了彻底干掉锁Linux 内核引入了per-CPU 变量。它的核心思想极其纯粹既然大家抢同一个变量会打架那我就给每个 CPU 核心都复制一份一模一样的变量私有副本。无锁化原理当内核代码需要操作某个 per-CPU 变量时它通过当前执行代码的 CPU 编号CPU ID作为索引直接去访问属于该 CPU 自己的那份内存。各玩各的互不干扰CPU 0 只读写变量的“副本 0”CPU 1 只读写“副本 1”。因为在空间上完全隔离根本不存在多核竞争因此完全不需要加锁。硬件级优化内核还会确保每个 CPU 的副本都在独立的缓存行Cache Line上避免了假共享False Sharing让 CPU 能以接近硬件极限的速度本地访问数据。例如内核中各个 CPU 的网络收发包计数器、内存分配器的本地缓存PCP都极度依赖这种机制。2. 现状之痛x86 与 Arm 的天壤之别虽然 per-CPU 变量在逻辑上实现了无锁但在具体的汇编指令级实现上不同 CPU 架构却拉开了巨大的差距。这就是this_cpu操作专门用于快速访问当前 CPU 私有变量的宏需要解决的核心问题。x86 的硬件外挂段寄存器在 x86 架构上每 CPU 访问可以通过在相关指令前加上段寄存器前缀如gs或fs来实现。代码段# x86 汇编示例一步到位 movl %gs:per_cpu_variable_offset, %eax优势MMU内存管理单元在硬件层自动处理基地址偏移这使得整个访问变成了一个单指令操作。由于单指令在单核上天然具备原子性执行过程中不可能被中断或抢占安全且极快。Arm 等架构的无奈多指令序列包括 Arm 和 RISC-V 在内的许多现代架构并没有类似于 x86 的段寄存器机制。劣势它们必须在软件层分步执行——第一步获取当前 CPU 的 ID 并计算出地址偏移第二步通过计算出的虚拟地址去读写数据。这直接将一个原本简单的操作变成了多指令序列。性能的牺牲者抢占Preemption多指令序列带来了一个致命的并发漏洞如果线程在第一步计算出地址之后、第二步尚未读取数据之前被内核抢占或者该线程被调度迁移到了另一个 CPU 上那么当它恢复执行第二步时它访问的依然是旧 CPU 的数据导致 Data Corruption。为了堵住这个漏洞Arm 等架构在执行this_cpu操作时必须显式地禁用内核抢占Disable Preemption。频繁地关闭和开启抢占成为了非 x86 架构上的一大性能包袱。3. Yang Shi 的破局方案每 CPU 页表与双重映射为了彻底消除 Arm 等架构上的地址计算开销与禁用抢占的包袱Yang Shi 提出了一个大胆的设想利用 MMU 的页表黑魔法让同一个内核虚拟地址在不同的 CPU 上自然指向不同的物理内存。其核心设计包含以下几个要点消除索引计算通过动态配置每 CPU 页表任何给定的每 CPU 变量在所有 CPU 核心上都拥有完全相同的虚拟地址例如都是0xffff888000001000。CPU 访问时直接读写该地址即可硬件会自动映射到不同的物理页。由于不需要计算偏移抢占自然也不再是问题。双重映射Dual Mapping解决初始化难题由于变量在各 CPU 上的虚拟地址相同这会破坏用于跨 CPU 初始化数据的per_cpu_ptr()宏该宏需要能遍历所有 CPU 的数据。为此Shi设计了双重映射机制全局映射保留现有的全局内核映射专门用于跨 CPU 的初始化与全局管理。私有映射针对每个 CPU 建立特定的每 CPU 页表映射用于日常的高速无锁访问。4. 为什么 Linus Torvalds 曾强烈反对这种设计正如与会的 Jason Gunthorpe 所指出的使用每 CPU 页表并不是什么新发明。在过去Linux 掌门人 Linus Torvalds 曾对此类方案持强烈反对态度。如果无法妥善解决 Linus 的顾虑这项特性将很难被合并进主线。Linus 的极力反对主要基于以下三大“工程梦魇”① TLB转译后备缓冲器的管理灾难页表的核心加速器是 TLB页表缓存。在传统设计中内核地址空间在所有 CPU 间是共享且全局Global的。如果引入每 CPU 页表意味着同一虚拟地址在不同 CPU 上对应不同的物理页。一旦某个每 CPU 变量被释放或内核动态调整了内存映射内核就必须极其精确地发起TLB 刷新TLB flush。在庞大的多核系统上管理这种“因核而异”的内核 TLB 条目极易出错稍有不慎就会引发难以调试的硬件级内存损坏Stale TLB entries。② 多核扩展性死锁IPI 频繁Linus 一向对任何可能导致多核扩展性暴跌Scalability issue的设计高度警惕。如果每个 CPU 都有自己独立的内核页表副本那么当内核动态分配、释放内存或者修改了全局内核地址空间时内核就必须把这些修改同步复制到所有 CPU 的页表中。在动辄上百核的服务器上这会引发大量的处理器间中断IPI和严重的锁竞争得不偿失。③ 历史阴影32位时代的kmap_atomic内核历史上吃过类似的亏。在 32 位 x86 时代为了访问高于 1GB 的内存Highmem内核引入了kmap_atomic。它也是为每个 CPU 保留一组专用的页表条目来进行临时映射。这个机制后来成为了内核维护者的噩梦代码晦涩、死锁频发。Linus 极其厌恶这种在内核态“动态倒腾页表”的设计。5. 2026 的技术天平不可忽视的庞大收益既然有如此大的历史阻力为什么 Yang Shi 依然选择在 2026 年旧事重提答案在于硬件的演进与无法拒绝的性能跑分。成本在可控范围内随着现代处理器架构的迭代硬件对 TLB 的管理如 ARM 的 ASID/PCID 进程上下文 ID 匹配机制已经大幅改善。Shi 指出该方案的内存开销极小在具有160 个核心的大型机器上每 CPU 页表仅需占用约2MB的物理内存。虽然它确实会带来额外的页表分配操作且在 32 位老旧架构上可能存在虚拟地址空间紧张的问题但在如今 64 位服务器主导的时代这些副作用已不再致命。惊艳的基准测试结果在 160 核 Arm 系统上跑出的性能数据让会场上的专家们如 Brendan Jackman大为震惊内核构建基准测试Kernel-build系统时间System Time大幅减少了13-18%实际执行时间Wall-clock Time缩短了3-7%。stress-ng 基准测试同样展现出了极为显著的性能压制。如此庞大的系统级时间红利单靠 Mini 避免禁用抢占是解释不通的任何涉及多于一条指令的更新类型操作仍然需要在禁用抢占的情况下执行。背后必然还涉及到了更深层次的 CPU 流水线指令深度优化和缓存命中率改善这使得内核社区不得不重新严肃审视该方案。6. 未来展望与替代路径随着会议进入尾声开发者们针对该方案的未来和替代方案展开了激烈的头脑风暴内核内可重启序列Restartable SequencesRyan Roberts 提出由 Peter Zijlstra 主导的内核内rseq工作是否能作为无锁多指令序列的替代方案由于 Shi 对此尚不深入这成为了后续的研究方向。NUMA 文本复制NUMA Text ReplicationShi 指出一旦每 CPU 页表的框架搭建成功未来还可以顺理成章地用于在不同的 NUMA 节点之间复制内核核心代码文本Kernel Text实现真正意义上的全系统本地高速访问。联动 “mermap”Brendan Jackman 也兴奋地表示每 CPU 页表的引入将完美助力他此前在大会上提出的 “mermap” 内存映射优化提案。结语在 Linux 内核的长青树下没有什么是不可动摇的即使是 Linus 曾经给出的定论。Yang Shi 的这项提案正是现代多核 ARM 服务器兴起背景下底层架构对绝对性能的一次勇敢突围。在 13-18% 的巨大系统级优化面前内核社区究竟会通过精妙的工程手段驯服 TLB 刷新的恶魔还是寻找更温和的替代方案让我们拭目以待这场 2026 年内核最精彩的架构变革之争。