按精确到期时间组织高精度定时器
hrtimer 通过 timerqueue 的红黑树维护到期顺序,并缓存最早到期节点。
工程问题#
高精度定时器需要频繁插入、取消和确定下次唤醒时刻;超时值分布不一定落在规则的粗粒度时间槽中。
实际实现#
enqueue_hrtimer 将节点交给 timerqueue_add;后者调用 rb_add_cached。树按 expires 比较,缓存最左节点便于读取下一到期项。
处理流程#
- 选择对应时钟和 CPU 的 timer base
- 按 expires 插入红黑树
- 读取最早到期事件
- 到期出队后执行回调或重新入队
源码片段#
固定版本 · 原文摘录20static inline bool __timerqueue_less(struct rb_node *a, const struct rb_node *b)21{22 return __node_2_tq(a)->expires < __node_2_tq(b)->expires;23}2425/**26 * timerqueue_add - Adds timer to timerqueue.27 *28 * @head: head of timerqueue29 * @node: timer node to be added30 *31 * Adds the timer node to the timerqueue, sorted by the node's expires32 * value. Returns true if the newly added timer is the first expiring timer in33 * the queue.34 */35bool timerqueue_add(struct timerqueue_head *head, struct timerqueue_node *node)36{37 /* Make sure we don't add nodes that are already added */38 WARN_ON_ONCE(!RB_EMPTY_NODE(&node->node));3940 return rb_add_cached(&node->node, &head->rb_root, __timerqueue_less);41}比较函数以 expires 建立到期顺序,rb_add_cached 插入节点并维护最左缓存。hrtimer 的 enqueue_hrtimer 在 kernel/time/hrtimer.c 调用此库函数;返回值说明新节点是否成为最早到期节点。
1073/*1074 * enqueue_hrtimer - internal function to (re)start a timer1075 *1076 * The timer is inserted in expiry order. Insertion into the1077 * red black tree is O(log(n)). Must hold the base lock.1078 *1079 * Returns 1 when the new timer is the leftmost timer in the tree.1080 */1081static int enqueue_hrtimer(struct hrtimer *timer,1082 struct hrtimer_clock_base *base,1083 enum hrtimer_mode mode)1084{1085 debug_activate(timer, mode);1086 WARN_ON_ONCE(!base->cpu_base->online);10871088 base->cpu_base->active_bases |= 1 << base->index;10891090 /* Pairs with the lockless read in hrtimer_is_queued() */1091 WRITE_ONCE(timer->state, HRTIMER_STATE_ENQUEUED);10921093 return timerqueue_add(&base->active, &timer->node);1094}完整 hrtimer 入队函数与复杂度注释:调用者必须持有 base 锁,状态更新后把节点加入 timerqueue。与已有 lib/timerqueue.c 片段共同证明 hrtimer 的真实调用关系。
两个连续片段分别展示 timerqueue 库和实际 hrtimer 调用方。库本身不加锁,enqueue_hrtimer 要求调用者持有 base 锁;片段不展示时钟事件编程与回调执行。
收益与取舍#
工程分析带来的收益
- 保留完整的到期顺序
- 缓存最小节点减少重复遍历
付出的代价
- 工程分析:插入删除需要维护树平衡
- 精确队列不能消除中断与执行延迟
可比较的选择
Linux 普通时间轮以较粗时间槽组织大量通常会被取消的普通超时,固定层数下选槽和挂入成本不随定时器数增长;v6.12 不再采用经典级联迁移。
QEMU 有序定时器链表QEMU v9.2.0 的相应实现读取表头直接,插入按到期时间线性扫描 O(n);与红黑树不同,适用取舍需结合各自定时器数量与运行环境。
版本与适用范围#
固定 Linux v6.12 的 hrtimer 子系统。它按到期时间维护红黑树,并缓存最左节点;普通 timer_list 则使用非级联多层时间轮。纳秒单位表示不等于每次回调都有纳秒级的实际精度,实际触发还受时钟事件设备、CPU 状态与调度影响。
参考来源#
源码、官方文档与原始论文相关实现与比较#
4 个相关案例Linux 普通定时器把较近超时放进细粒度槽,远期超时放进粗粒度槽,使用位图寻找待处理槽。v6.12 的这套时间轮不再逐层级联迁移。
QEMU 的 QEMUTimerList 用有序单链表维护定时器,链表头就是下一到期项。
nginx 事件定时器以到期时间为键放入红黑树,事件循环读取最小键确定等待上限。
Linux 公平调度将 EEVDF 的资格条件与虚拟截止时间结合,用增强红黑树搜索候选任务。