跳转至

W15 - 实习总结:异步驱动三个月,从 D1 串口到 SMP=16 网卡

周期:2026-09-26 ~ 2026-10-02(实习总结,覆盖 2026-06-21 ~ 2026-09-27) 仓库:StarryOS(daivy2333/StarryOS,mul-hart-k3 分支)

实习内容是在 StarryOS 上做异步驱动开发。三个月里设计了异步驱动的统一架构:串口在 Lichee RV Dock D1 真板上正常收发(64B 短包 96.6% 线速),网卡在 QEMU 十六核(SMP=16)下 TCP/UDP 双向正常、poll/select 就绪可用、故障注入后可恢复。9/26 工作收口,9/27 迁移项目文档体系。实习期间共 153 个提交、14 篇周报、42 篇学习笔记。

实习概况

项 内容
时间 2026-06-21 ~ 2026-09-27(14 周)
项目 StarryOS:Rust 编写的宏内核,基于 ArceOS 组件化架构
方向 异步驱动开发:UART 串口 → VirtIO 网卡,QEMU 与真板双线取证
代码 153 个提交,主线 mul-hart-k3(前期为 uart-16550-lichee、net-k3)
记录 周报 W01–W14、学习笔记 42 篇(本仓 docs/notes/ 四个分区)

实习之前的训练营记录在 2026sOsReport,本仓从实习第一天开始记的。

驱动架构设计

通用骨架。把"中断进来到应用拿到数据"拆成触发链四段:ISR 收中断、读寄存器识别原因、唤醒 waker、调度异步任务。四段跨设备同构,配四条契约:ISR 不搬数据,只做原因确认、清中断、唤醒;waker 注册后立即重查硬件状态,防丢唤醒;每轮工作量有上限,积压显式可见;返回等待前先释放锁再唤醒。串口和网卡的骨架遵循同一套契约;数据结构层因设备而异(字节流 vs 整包),不做强行抽象。网卡开发与测试全程,异步串口一直作为控制台承担系统输入输出——SMP=16 那轮运行的记录本身就是经串口打印的,两个驱动在同一内核里共存工作,说明这套骨架契约可以支撑更多同路径异步驱动同时共存。

串口:双环形缓冲 + 双后台任务。收发各用一个无锁 SPSC 环形缓冲,RX/TX 两个 copier 任务在缓冲与硬件间搬数据,ISR 只读状态、关中断、唤醒,向上经 TTY 行规程接到用户 read/write。空闲时全链路睡眠,数据密集时 copier 切轮询连续抽干,把中断次数压到最低;TX 方向多一层 tcdrain 排空等待和慢轮询回退。驱动通过 OsRuntime/OsWakerSet 两个 trait 对接操作系统,不依赖具体任务与轮询实现,换平台只换适配层。

网卡:五层 + 发送记录 + 恢复语义。从硬件到应用五层:ISR 只清中断唤醒;队列 owner 是唯一后台任务,收(reap/refill)与发(submit/reclaim)预算制推进;栈 runner 常驻推进 smoltcp 的收发、维护与定时器;就绪桥把单槽 waker 展开成多等待者,socket 就绪状态接到 poll/select。收发以描述符和固定槽位为所有权单位,内存有硬上界;每次发送带代际号与序号,提交、完成、回收三处计数闭合后缓冲区才复用,迟到的完成通知不跨代生效。恢复按等待者取消、提交前撤销、设备持有三段区分,超时与设备复位分阶段处理,复位必须确认设备状态清零后才重建队列。

多核:绑核 + 本地优先唤醒。K3 的 AP 域有 8 × X100 + 8 × A100 共 16 个核,两个驱动的后台角色固定绑核(角色 i 落在可调度集合第 (anchor + i) 个),绑定在入队前完成。内核临界区从"只关本地中断"升级为"本地中断恢复 + 全局互斥 + 每核嵌套计数",调度组件增加入队前绑核与远端唤醒 IPI。唤醒优先本地完成,必须跨核时恰好发一次 IPI,避免唤醒风暴。另加两组快照观测(串口 152 字节、网卡 V5),可直接读出"谁跑在哪个核"。

落地过程

阶段一:异步串口 + D1 真板(6/21 ~ 7/25)。拆分真板验证目标并行推进;新建 D1 平台组件,补页表属性、early console、中断控制器处理,隔离真板上不存在的 virtio/PCI 路径;端到端打通 /dev/console、TTY、tcdrain、非阻塞 I/O,userbench 在真板跑通。修掉一批只在真板暴露的问题:THRE 边沿丢失导致 tcdrain 不返回、64B 标准输出积压污染测量(修完 D1 64B 从约 1 KB/s 提到 11.13 KB/s)、TX copier 预算耗尽卡死。另做 Console 轮询对照,量化异步方案的 CPU 收益。7/25 串口主线文档归档。详见 M00 月报 和 W01 ~ W05。

阶段二:异步网卡,QEMU 单核(7/26 ~ 9/4)。按"同步接通 → 轮询 → 中断诊断 → 收包异步化 → 双向有界数据面 → 协议栈自主推进 → poll/select 就绪 → 故障恢复"的顺序逐步落地,每步只变一个主要改动:7/29 同步方式接通协议栈、VirtIO-MMIO 轮询回退;8/3 中断诊断链路;8/6 轮询性能对照;8/12 收包异步化;8/19 双向有界数据面(12 轮开发,27 项任务);8/27 应用可直接 poll/select 的异步网络;9/2 故障恢复语义收口,注入等待者取消、提交前撤销、设备复位、链路抖动等故障,无重复释放、永久挂起或静默丢包。详见 W06 ~ W11。

阶段三:K3 板与多核(9/5 ~ 9/27)。K3(进迭时空)板卡接线、SSH 连通、工具链配置,资料沉淀到 daivy2333/k3。9/16 定多核适配方案后按上面「多核」设计实施,过程中修掉 TX 丢唤醒(waker 注册后重查重试)、release 构建越界读出幽灵核(掩码类型加容量检查)。9/26 SMP=16 六组场景跑通(见下节)。9/27 提交验证装置(内核探针 + 宿主机对端脚本 + 纯输出校验器)与复现手册(5df918cd)。

运行效果

以下数据全部取自仓库留档的真实运行记录。

串口:D1 真板收发(2026-07-07 基准,115200 bps 物理线速)

场景 实测输出
64B 短包写 + 排空 ×100 11.13 KB/s,96.6% 线速,short_writes=0,排空错误 0
256B / 1024B 写 + 排空 ×100 97.3% / 98.8% 线速
1024B 批量排空(8 写 1 排) 99.1% 线速
单字节发送延迟 ×100 平均 0.186 ms
非阻塞读(O_NONBLOCK 与 FIONBIO 两种方式开启) PASS,空读正确返回 EAGAIN
内核态入队速度 64B 包 4.2 MB/s(约物理线速的 368 倍,写入不阻塞)
接收路径微基准 RX 环形缓冲读 8.4 GB/s,读取延迟 P99 246 ns
整轮基准 进程 exit 0

真板启动日志同时确认:MMIO 映射(stride=4)、IRQ 18 注册成功、双 64 KB 缓冲就位。完整输出留档于 StarryOS 仓库 .agents/analysis/_archive/2026-07-11-q19c-d1-async-uart-closeout/lichee/。

网卡:SMP=16 六组场景(2026-09-26 运行,对应 K3 的 16 核)

网卡队列 owner 固定在 hart 15,协议栈 runner 固定在 hart 0,串口 RX/TX copier 分别在 hart 13/14:

场景 实测输出
绑核检查 owner/runner 单例绑核、实际运行核与绑核一致、零拒绝,PASS
关闭定时器下的跨核唤醒 唤醒完成 1 次、定时器恢复、无丢失;IPI 发送 = 接收 = 191
TCP 双向 客内 socket 发 8 收 8,宿主机对端 ok;提交 11 = 完成 11 = 回收 11,PASS
UDP 双向 客内 socket 发 8 收 8;提交 21 = 完成 21 = 回收 21,PASS
队列满载恢复 压满 64 槽再释放,恢复后发送 322 = 完成 322 = 回收 322、在途 0,PASS
空闲安静窗口 poll 就绪与实际 I/O 一致,静默期数据面无动作,PASS

六组场景全部 PASS,探针退出码 0;宿主机抓包文件证实双向 echo 帧数与客内 socket 计数逐项一致。串口多核冒烟同轮通过:远端唤醒字节送达、IPI 因果计数 4 = 4、TX 环形缓冲收敛。留档于 StarryOS 仓库 openspec/changes/archive/2026-09-26-ms08-qemu-multi-hart-correctness-baseline/evidence/005-per-driver-smp16-fixed-qualification/003-rework/(guest 运行记录 + 抓包文件)。

参考