
__per_cpu_offset数组是访问 per-CPU 变量的核心机制它的作用相当于一张地图记录了每个 CPU 的 per-CPU 数据区域在整个内存中的位置偏移量。核心概念per-CPU 变量的存储模型为了理解__per_cpu_offset首先要明白 per-CPU 变量在内存中是如何布局的。在代码中通过DEFINE_PER_CPU(int, x)这样的宏定义的变量其实并不是一个真正的变量而更像是一个模板或蓝图。内核在初始化时会为系统中每一个可能的 CPU都在内存中分配一块独立且大小相同的数据区域称为.data..percpu段。所有通过DEFINE_PER_CPU定义的变量都会在这个区域内为每个 CPU 各保留一份独立的副本。__per_cpu_offset的工作原理__per_cpu_offset数组就是用来定位这些副本的。数组的定义与作用__per_cpu_offset是一个全局数组定义在include/asm-generic/percpu.h中其大小通常等于系统支持的最大 CPU 数量NR_CPUS。数组的每个元素__per_cpu_offset[n]存储的是第 n 号 CPU 的 per-CPU 数据区域相对于某个基准地址通常是CPU0的数据区起始地址的偏移量。访问变量的核心公式当内核代码比如在 CPU 3 上运行想要访问自己的x变量时会使用per_cpu_ptr(x, cpu)这样的宏。它的核心逻辑是变量在CPU 3上的实际地址 x (这个“模板”的地址) __per_cpu_offset[3]。也就是说通过将变量的模板地址加上当前 CPU 对应的偏移量就得到了该变量在当前 CPU 上那份独立副本的真实内存地址。底层优化硬件级别的加速通道__per_cpu_offset数组提供了一种通用的、与架构无关的寻址方式。但在性能攸关的场景尤其是访问当前CPU 的 per-CPU 变量时内核会利用硬件特性进行极致的优化从而绕开这个数组。x86 架构使用gs段寄存器在 x86 架构上内核会将当前 CPU 的 per-CPU 数据区域的基地址直接加载到gs段寄存器中。这样一来访问当前 CPU 的变量x就可以编译成一条简单的指令例如mov eax, gs:[x]。这里的x被视为一个相对于gs段基址的偏移量无需再经过__per_cpu_offset数组的计算。这种方式不仅效率极高还天然避免了进程在不同 CPU 间迁移导致的竞态问题。ARM64 架构使用tpidr_el1寄存器类似地在 ARM64 架构上内核会使用tpidr_el1这个特殊的寄存器来保存当前 CPU 的 per-CPU 数据基址。访问时通过mrs指令读取该寄存器并结合变量的偏移量来获得最终地址。所以__per_cpu_offset数组是理解 per-CPU 变量设计哲学的基石它清晰地展示了如何通过一个偏移量来实现一份模板多份副本的机制。而在实际运行时为了达到极致性能硬件特性如 x86 的gs段寄存器会作为更快的专线直接、高效地完成地址转换。