ax-ipi
路径:
os/arceos/modules/axipi类型:库 crate 分层:ArceOS 层 / IPI 运行时基础件 版本:0.3.0-preview.3文档依据:Cargo.toml、README.md、src/lib.rs、src/event.rs、src/queue.rs
ax-ipi 是 ArceOS 的跨核回调分发模块。它基于 ax-hal 的 IPI 发送能力,为每个 CPU 维护一个本地事件队列,并向上暴露“在某个 CPU 上运行闭包”或“在所有其他 CPU 上广播闭包”的接口。它属于运行时叶子基础件:负责 IPI 事件排队和派发,不负责 SMP bring-up、调度策略或通用消息总线。
架构设计
设计定位
ax-ipi 的核心目标不是实现一个复杂的多核通信框架,而是提供一条很短的工作链:
- 把闭包包装成可发送的 IPI 事件。
- 放入目标 CPU 的本地队列。
- 通过
ax-hal::irq::send_ipi()触发对方 CPU 进入 IPI 中断。 - 在 IPI handler 中把队列里的事件逐个取出执行。
因此,ax-ipi 更像“IPI 回调投递器”,而不是调度器、work queue 或通用 RPC 层。
模块结构
src/lib.rs:初始化、单播/广播发送和 IPI handler 主线。src/event.rs:回调封装,区分单次消费的Callback与可克隆广播的MulticastCallback。src/queue.rs:基于VecDeque的IpiEventQueue,以 FIFO 顺序存放待处理事件。
1.3 关键对象
Callback:Box<dyn FnOnce()>封装的单播回调。MulticastCallback:Arc<dyn Fn()>封装的广播回调,可拆成多个单播回调。IpiEvent:记录源 CPU ID 与具体回调。IpiEventQueue:每 CPU 一个的待处理事件队列。IPI_EVENT_QUEUE:通过#[ax_percpu::def_percpu]声明的每 CPU 静态LazyInit<SpinNoIrq<IpiEventQueue>>。
1.4 发送与处理主线
发送到单个 CPU 的流程如下:
实现里的重要细节:
run_on_cpu()遇到目标就是当前 CPU 时不会排队,而是同步立即执行。run_on_each_cpu()会 先在当前 CPU 上立刻执行一份,再把 clone 后的回调投给其他 CPU。ipi_handler()会循环 drain 当前 CPU 队列,而不是只处理一个事件。
1.5 能力边界
ax-ipi队列是 FIFO,但不提供优先级、取消、重试或返回值汇总。- 回调运行在 IPI 处理上下文里,默认应保持短小且不可阻塞。
- 这个 crate 没有自己的 feature 门控,但它依赖
ax-hal已开启ipi能力。
核心功能
功能概览
- 初始化每 CPU 的 IPI 队列。
- 向指定 CPU 投递单次回调。
- 向所有其他 CPU 广播回调。
- 在 IPI 中断处理函数里取出并执行待处理事件。
使用场景
init():由ax-runtime/src/mp.rs在次核 bring-up 路径中调用,为当前 CPU 建立 IPI 队列。ipi_handler():由ax-runtime/src/lib.rs的 IRQ 处理路径调用。run_on_cpu()/run_on_each_cpu():是这个 crate 的核心公开 API,也是ax-api/ax-feat暴露 IPI 能力的底层基础。
边界说明
- 它不是 SMP 启动器;启动 CPU 的逻辑在
ax_runtime::start_secondary_cpus()。 - 它不是通用异步执行框架;没有 future、返回值或 work stealing。
- 它也不是调度器;闭包何时运行只受 IPI 到达和 handler 执行控制。
依赖关系
通常只有在上层 feature 打开 ipi 时,最终镜像才会真正把它编进去。
注意事项
init()是每 CPU 初始化,不是全局初始化;修改时必须同时考虑 BSP 和 AP 路径。run_on_each_cpu()当前包含“立即执行当前 CPU”这一步,修改时不能无意改变这个语义。Callback/MulticastCallback的封装关系要保持清晰,避免把广播路径退化成共享可变闭包。- 不要把复杂的等待、应答、重传协议塞进
ax-ipi;这层应该继续保持单纯。