第二十五周工作总结
概述
本周完成了 rt-async-amp 双核 AMP(非对称多处理)项目的整体搭建与 IPC 通信链路打通。项目在 QEMU virt RISC-V 64 平台上实现了 StarryOS(S-mode, hart 0)+ rt-async(M-mode, hart 1) 的双核异构架构,包含完整的启动链路、共享内存 IPC、事件驱动中断通知机制。
本项目参考了之前的 embassy_preempt_VisionFive2 方案,在项目架构和实现方式上做了系统性改进。
项目架构
硬件平台
| 旧项目 (VisionFive2) | 新项目 (rt-async-amp) | |
|---|---|---|
| 平台 | JH7110 异构五核 (1×S7 + 4×U74) | QEMU virt 双核 |
| RTOS 核心 | S7 (RV64IMAC, 无 CAS, 小缓存) | 标准 riscv64 核心 |
| OS 核心 | U74×4 (RV64GC) | 标准 riscv64 核心 |
| 实时侧特权级 | S-mode | M-mode(零开销中断) |
与旧项目的对比改进
1. 项目架构:从松散拼接到统一构建
旧项目:embassy_preempt、StarryOS、OpenSBI、U-Boot 各自独立仓库,通过顶层 Makefile 串联编译。配置常量(地址、大小)分散在各子项目中手动维护,容易出现不一致。
新项目:
amp.toml作为 single source of truth,集中管理所有物理地址、内存布局、ioctl 编号等常量xtask构建工具读取amp.toml,自动生成 Rust 代码(amp_gen.rs)和 OpenSBI patch 模板替换- 确保所有组件(rt-async、StarryOS、用户态程序)的配置始终一致
2. RTOS 实现:从回调式到纯 async/await
旧项目 (embassy_preempt):
- 任务定义为
fn task(args: *mut c_void),同步任务用函数指针,异步任务通过状态机 - 调度基于手动上下文切换(
IntCtxSW),任务通过 TCB(Task Control Block)管理 - IPI 等待通过
IpiWaitList(固定 4 槽位数组)+ 回调ipi_callback实现 - 平台耦合:executor 直接引用
GlobalSyncExecutor、OS_TCB_REF等全局状态
新项目 (rt-async):
- 任务定义为
async fn,通过#[executor::task]宏自动转换为SpawnToken - 调度基于 Rust Future 机制 + 优先级抢占,无需手动上下文切换
- 平台抽象:
Chiptrait +TimerChiptrait +extern_trait零成本抽象,executor 不依赖具体硬件 - 用户态级别的
WaitForMessageFuture 直接检查共享内存,不需要 executor 内部的 wait list
3. IPC 模型:从 wake_all 到精确通知
旧项目:
rust
// ipi_callback — 中断中唤醒所有等待任务
fn ipi_callback(_ctx: *mut ()) {
IPI_WAIT_LIST.wake_all(); // 唤醒所有等待者,不区分是否有数据
GlobalSyncExecutor().IntCtxSW(); // 手动触发上下文切换
}- IPI 到来时
wake_all()唤醒所有等待任务,不检查是否有真实数据 - 最多支持 4 个任务同时等待
- 任务引用通过裸指针管理,需要手动生命周期
新项目:
rust
// notify_from_isr — 中断中检查共享内存后精确唤醒
pub fn notify_from_isr() {
if !intercom::has_pending() { return; } // 无数据则不唤醒
// ... wake registered waker
}
// task_ipc — 两行循环完成整个 IPC 处理
loop {
WaitForMessage.await;
while process_pending() {}
}- ISR 中检查共享内存
has_pending(),仅在真实数据存在时唤醒,零伪唤醒 - 基于 Rust 标准 Waker 机制,无固定槽位限制
- 全关中断保护 waker 注册,无竞态
4. IPC 协议:从原始共享内存到结构化 RPC
旧项目:通过 intercom 模块手动读写共享内存,无结构化协议。
新项目:
ov-channels:无锁双通道共享内存 ring buffer(SQ/CQ 模型)ov-rpc:基于共享内存的 RPC 框架,define_service!宏定义服务接口- 用户态通过
/dev/rt_shm设备 mmap 共享内存 + ioctl 发送/等待 IPI - rt-async 侧通过
process_one()逐条处理,支持 RPC 和通知两种消息类型
5. 内存安全与正确性
新项目增加的保障:
- 双向 IPI 发送前均有
fence(Release)保证共享内存写入对端 hart 可见 mmap长度从align_down改为align_up,修复非页对齐 device region 映射截断 bugSHMSIZE页对齐(67072 → 69632),跨组件通过amp.toml自动同步- StarryOS CLINT 映射通过
axconfig.toml的mmio-ranges显式声明,不依赖 OpenSBI patch 的隐式映射
核心工作详述
OpenSBI AMP hart 路由
在 fw_base.S 的 _start 中添加 hart 路由逻辑:hart 1 直接跳转 rt-async 入口(0x82800000),跳过 OpenSBI 初始化,以 M-mode 运行。hart 0 走正常 OpenSBI → StarryOS 流程。
WaitForMessage Future
ipc_wait.rs 实现事件驱动 IPC 等待:
WaitForMessage是 zero-size Future,poll 时检查共享内存has_pending()- 无数据:关中断 → 注册 waker → 二次检查 → 开中断 → Pending
notify_from_isr()仅在has_pending()为 true 时 wake waker
executor-macro 中断分发
__Inner_MachineSoft 移到 MachineSoft ISR 开头(clear_pend 之前),使得 notify_from_isr() → wake → pend → 同一次中断内调度执行。
StarryOS 侧
rt_shm设备提供/dev/rt_shm,支持 mmap(共享内存)、ioctl(NOTIFY/AWAIT/CLR_PENDING)send_ipi_to_rt_async()直接写 CLINT MSIP1 +fence(Release)(低延迟路径)ipi_irq_handler()接收 hart 1 的 IPI,清除 SSIP,通知用户态