时间
Timer::after_millis(100).await 要回答三个问题:现在几点、100 ms 是多少个单位、到点了谁来叫醒任务。embassy 把这三件事拆进四个小 crate:
| crate | 版本(tag) | .rs 行数 | 回答什么 |
|---|---|---|---|
embassy-time | 0.5.1(embassy-time-v0.5.1) | 1615(含 270 行日志宏 fmt.rs) | 用户 API:Instant、Duration、Timer、Ticker、with_timeout、Delay;另带 std、mock、wasm 三个驱动 |
embassy-time-driver | 0.2.2(embassy-time-driver-v0.2.2) | 667(其中 488 行是脚本生成的 tick.rs) | 驱动接口:now、schedule_wake 两个函数,TICK_HZ 常量 |
embassy-time-queue-utils | 0.3.2(embassy-time-queue-utils-v0.3.2) | 351 | 给驱动用的定时器队列:集成队列和通用队列 |
embassy-executor-timer-queue | 0.1.0(无 tag,commit a416aac9c) | 104 | 执行器在每个任务头里留的那块队列项内存 |
硬件那一半在 HAL 里,本卷读 embassy-stm32 0.6.0 的 src/time_driver/(739 行)。
- Instant 与 Duration:两个类型都只是一个
u64tick;TICK_HZ由 161 个tick-hz-*feature 之一选出,不选就是 1 MHz,选两个编不过;换算先用 GCD 约分;Duration::from_*向上取整,Instant::from_*和所有as_*向下取整;as_nanos在 32768 Hz 下 9 年就溢出,release 里悄悄算错。 - 时间驱动:驱动接口在 0.2.0 收成两个函数,靠链接期符号
_embassy_time_now找到实现,没有驱动是链接错误,两个驱动在 rust-lld 下报重复符号,macOS 的 ld 却可能悄悄选一个;std 驱动用一个闹钟线程,mock 驱动手动推进时间;stm32 的 TIM 驱动用 32 位周期数加 16 位计数器拼时间,QEMU 里 TIM 没有模型,时间停在 0;模板里的 SysTick 驱动每毫秒中断一次,关中断期间丢 tick。 - 定时器队列:集成队列把链表节点放进任务头,从 waker 的 vtable 认出 embassy 任务;通用队列是一个定长
heapless::Vec,满了就提前叫醒一个,std 上 9 个定时器挤 8 个槽时,半秒内任务被 poll 近 400 万次;一个永不到期的定时器曾让同一任务之后的定时器全部失效。 - Timer、Ticker 与超时:
Timer第一次 poll 一定返回Pending;with_timeout在调用时就定下截止时刻;Ticker错过的 tick 一次性补上,std 和 QEMU 上都是 3 个 tick 同时到;block_for是忙等。
flowchart TB user["Timer Ticker"] --> time["embassy-time"] time --> drv["embassy-time-driver"] drv -.->|链接期符号| hal["HAL 时间驱动"] hal --> qu["queue-utils Queue"] qu --> etq["executor-timer-queue"] exec["embassy-executor"] --> etq etq -.->|链接期符号| exec
图里两条虚线都是链接期符号:embassy-time 调 _embassy_time_now / _embassy_time_schedule_wake,由 HAL 用 time_driver_impl! 定义;集成队列调 __embassy_time_queue_item_from_waker,由 embassy-executor 定义。所以 embassy-time 不依赖执行器,执行器也不依赖 embassy-time,两边经两个小 crate 接上。执行器在任务头里怎么摆这块内存、waker 怎么指回任务,见任务存储和状态与运行队列;一次 Timer::after 从头到尾在 QEMU 上的打点,见一次 Timer::after。和 tokio 对照:tokio 的定时器是运行时里一个分层时间轮,跟 IO 驱动一起 park(见定时器);embassy 没有运行时,时间轮的位置换成了 HAL 驱动里的一条链表和一个硬件比较通道。
💬 关联讨论
发起讨论加载中...