ax-percpu
路径:
components/percpu/percpu类型:库 crate 分层:组件层 / 每核局部数据基础设施 版本:0.2.3-preview.1文档依据:当前仓库源码、Cargo.toml、README.md、src/lib.rs、src/imp.rs、src/custom/*、components/percpu/percpu_macros/src/*
ax-percpu 是整个仓库里所有“当前 CPU 局部状态”能力的基础设施。它通过 .percpu 段、架构相关的每核基址寄存器和过程宏 #[def_percpu],让系统能够为每个逻辑 CPU 保持一份独立数据副本,并在运行时以极低成本访问“当前核”的那份数据。调度器、当前任务指针、CPU ID、虚拟化每核状态、SMP 辅助状态都建立在这套机制之上。
架构设计
设计定位
ax-percpu 解决的是 SMP/多核系统中一个基础而关键的问题:
- 同一静态变量需要每个 CPU 一份副本
- 当前 CPU 访问自己的副本应尽量快
- 初始化阶段要把模板数据复制到各 CPU 私有区域
因此它本质上是“每核局部静态数据运行时”,而不是普通同步原语库。
模块结构
src/lib.rs 根据 feature 选择三条实现路径:
| 路径 | 模块 | 场景 |
|---|---|---|
sp-naive | naive.rs | 单核或测试环境,退化为普通全局变量 |
custom-base | custom/mod.rs | 平台自管每核基址,库只负责访问模型 |
| 默 认 | imp.rs | 标准 SMP 模式,负责拷贝模板、设置寄存器和运行时访问 |
此外:
| 模块 | 作用 |
|---|---|
ax-percpu-macros | #[def_percpu]、符号偏移与架构汇编生成 |
custom/tp.rs | custom-base 模式下的线程指针/基址辅助 |
1.3 #[def_percpu]:最核心的接口
ax-percpu 对外最重要的能力不是一个普通函数,而是过程宏:
#[def_percpu]
这个宏负责:
- 把真实存储放入
.percpu段 - 生成当前 CPU 的访问入口
- 生成偏移量与原始指针访问能力
- 在特定架构上生成高效的当前核访问汇编
从使用者视角看,它让“每核静态变量”像普通静态变量一样易用;从实现视角看,它其实是一套链接、初始化和寄存器访问协议。
1.4 运行时核心模型
标准 SMP 模式下,运行时模型可概括为:
- 链接脚本把
.percpu模板放入镜像 - 系统为所有 CPU 预留若干等大小 per-CPU 区域
- 启动时把 CPU0 模板复制到每个 CPU 对应区域
- 每个 CPU 把自己的区 域基址写入架构相关寄存器
#[def_percpu]生成的访问代码用“基址 + 变量偏移”定位当前核副本
这是一种典型、成熟且高性能的 per-CPU 数据实现方式。
1.5 imp.rs:标准实现主线
默认实现中最关键的几个能力是:
percpu_area_size()percpu_area_num()percpu_area_base(cpu_id)init()init_percpu_reg(cpu_id)read_percpu_reg()write_percpu_reg()
其中:
init()负责准备模板复制和整体区域初始化init_percpu_reg()负责把某个 CPU 的 per-CPU 基址写入寄存器read/write_percpu_reg()是所有“当前 CPU 局部访问”的硬件入口
1.6 架构差异
当前仓库里,per-CPU 基址的实现会根据架构切换:
- x86_64:GS base 路径
- AArch64:
TPIDR_EL1,在arm-el2下切换到TPIDR_EL2 - ARMv7:
TPIDRURO - RISC-V:
gp - LoongArch:专用通用寄存器
这说明 ax-percpu 虽然是通用基础件,但其运行时性能与正确性高度依赖架构相关寄存器约定。
1.7 custom-base 与 sp-naive
custom-base
适合平台自己掌管每核基址、而不完全走默认 runtime 模型的场景。此时:
ax-percpu仍然提供数据布局与访问接口- 但基址来源由外部符号或平台逻辑提供
这一模式对动态平台、定制 HAL 或特殊引导流程尤其重要。
sp-naive
这是最轻量的退化路径,用于:
- 单核
- host-side 测试
- 不需要真实每核副本的简单环境
它牺牲真实多核语义,换来更简单的构建和测试体验。
核心功能
功能概览
- 定义每 CPU 一份的静态数据
- 初始化 per-CPU 数据区
- 设置和读取当前 CPU 的 per-CPU 基址寄存器
- 为当前 CPU 提供高效局部访问接口
- 支持单核退化模式、EL2 模式、非零 VMA 和自定义基址模式
使用场景
| 场景 | 用法 |
|---|---|
| CPU 标识 | 用 #[def_percpu] 保存 CPU_ID、是否 BSP |
| 当前任务指针 | 保存当前核正在运行的 task 指针 |
| 调度器状态 | 保存 run queue、idle task、时钟状态等 |
| 虚拟化每核状态 | 保存当前物理核上的 VMM/per-CPU VMX/EL2 状态 |
| SMP 辅助数据 | 保存 IPI、启动辅助或局部缓存数据 |
2.3 典型初始化链路
在 ArceOS/Axvisor 体系里,典型主线是:
- 平台或运行时早期完成基本内存和 CPU 初始化
- 调用
ax_percpu::init()准备 per-CPU 区域 - 对主核调用
init_percpu_reg(cpu_id) - 通过
#[def_percpu]变量写入 CPU ID、当前任务等字段 - SMP 次核启动后重复设置自己的 per-CPU 基址