内核需要”过一会儿做某事”——TCP 重传、watchdog、调度器时间片。定时器是底层时间机制的核心。
一、先看图
flowchart TD
subgraph 低精度
WHEEL[Timer Wheel<br/>层次哈希<br/>jiffies 精度]
end
subgraph 高精度
HRTIMER[hrtimer<br/>红黑树<br/>纳秒精度]
end
WHEEL --> JIFFIES[基于 jiffies<br/>毫秒级]
HRTIMER --> CLOCKEV[clock_event_device<br/>硬件定时器]
classDef low fill:#f0883e22,stroke:#f0883e,color:#adbac7;
classDef high fill:#3fb95022,stroke:#3fb950,color:#adbac7;
class WHEEL,JIFFIES low
class HRTIMER,CLOCKEV high
二、Timer Wheel(低精度)
2.1 层次结构
struct timer_list {
struct hlist_node entry;
unsigned long expires; // jiffies
void (*function)(struct timer_list *);
};分层 wheel:
- Level 0:1 tick(1-4ms)
- Level 1:8 ticks
- Level 2:64 ticks
- …
越远的定时器放在越粗的层 → 到期时再细化。
2.2 使用
struct timer_list timer;
timer_setup(&timer, callback, 0);
mod_timer(&timer, jiffies + msecs_to_jiffies(100));
del_timer_sync(&timer);2.3 精度
jiffies 精度 = HZ 配置(100/250/1000 Hz)→ 1-10ms。
三、hrtimer(高精度)
3.1 数据结构
struct hrtimer {
struct timerqueue_node node; // 红黑树节点
ktime_t _softexpires; // 最早到期时间
enum hrtimer_restart (*function)(struct hrtimer *);
};3.2 使用
struct hrtimer timer;
hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
timer.function = callback;
hrtimer_start(&timer, ktime_set(0, 100000), HRTIMER_MODE_REL); // 100μs3.3 精度
取决于硬件(TSC、LAPIC timer、ARM generic timer)→ 纳秒级。
四、clock_event_device
硬件定时器的抽象:
struct clock_event_device {
void (*event_handler)(struct clock_event_device *);
int (*set_next_event)(unsigned long, struct clock_event_device *);
// ...
};hrtimer 编程下一个到期时间到 clock_event_device → 硬件在到期时触发中断。
五、tick 与无 tick
5.1 周期 tick
每 HZ 次/秒产生 tick 中断 → 更新 jiffies、检查定时器、调度。
5.2 动态 tick(NO_HZ_IDLE)
空闲时跳过 tick → 省电。
5.3 全无 tick(NO_HZ_FULL)
运行中也跳过 tick → 减少干扰。
六、Slack 与 Coalescing
timer_setup(&timer, callback, 0);
timer.slack = msecs_to_jiffies(50); // 允许 50ms 偏差内核合并临近的定时器到期 → 减少唤醒次数 → 省电。
/proc/timer_stats(已废弃)→
perf tracepoints 替代。
七、定时器迁移
CPU 即将下线 → 迁移该 CPU 的定时器到其他 CPU。
NO_HZ_FULL:把非关键定时器迁移到 housekeeping CPU → 隔离 CPU 无 tick 干扰。
八、用户态定时器
| API | 精度 | 通知方式 |
|---|---|---|
| sleep/usleep | 低 | 阻塞 |
| nanosleep | 高 | 阻塞 |
| setitimer | 低 | 信号 |
| timer_create | 高 | 信号/线程 |
| timerfd_create | 高 | fd(可 epoll) |
推荐 timerfd → 与事件循环组合。
九、观察
cat /proc/timer_list # 所有定时器(巨大输出)
cat /proc/timer_list | head -50 # 活跃 hrtimer
perf stat -e timer:hrtimer_start -a sleep 1
bpftrace -e 'tracepoint:timer:hrtimer_start { @[kstack(3)] = count(); }'十、小结
- Timer wheel:低精度(ms)、大量超时场景(TCP、缓存过期)
- hrtimer:高精度(ns)、调度时间片、timerfd
- clock_event_device 是硬件定时器抽象
- NO_HZ 跳过 tick → 省电、隔离
- 定时器迁移支持 CPU 热插拔和隔离
参考文献
kernel/time/timer.c(wheel)kernel/time/hrtimer.cDocumentation/timers/- Thomas Gleixner, “hrtimers and beyond.” OLS 2006
工具
/proc/timer_listperftimer tracepoints- bpftrace
- timerfd_create(2)
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
EEVDF 调度器:Linux 6.6 为什么换掉了 CFS
Linux 6.6 用 EEVDF 取代了 CFS 的 SCHED_NORMAL 选取逻辑。从 1995 年原始论文的 lag、eligibility、virtual deadline,到 commit 147f3ef 只重写 placement/pick/preempt,再到本机内核 6.6 上读 sched/debug 把每个任务的 vruntime、eligible 标志、deadline 一一对上 vd=ve+r/w,外加 nice 带宽与 base_slice 抢占两组实测,讲清换的是哪一块、延迟敏感任务凭什么先跑。
【操作系统百科】Windows 内核与 Linux 的关键差异
NT Executive/Kernel/HAL 分层、Object Manager 与 handle、IRP 驱动栈、IOCP 完成端口 vs epoll/io_uring、EPROCESS/KTHREAD vs task_struct、Registry vs 文件配置,以及 WSL2 为何选择 Hyper-V 轻量 VM。
【操作系统百科】实时 OS 巡礼
硬实时与软实时的可调度性边界、VxWorks/QNX/Zephyr 机制对照、Linux PREEMPT_RT 与主线合入、DO-178C/ISO 26262 认证语境,以及选型决策树。
【操作系统百科】Unikernel
Unikernel 在云上为什么没成主流?MirageOS、IncludeOS、Unikraft、OSv 的设计取舍——库操作系统、启动时间、工具链、调试困难、POSIX 兼容。