内核需要”过一会儿做某事”——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)
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。
【操作系统百科】交换
swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。
【操作系统百科】Slab/SLUB 分配器
buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。
【操作系统百科】用户态分配器
glibc malloc、tcmalloc、jemalloc、mimalloc 各有哲学。本文讲 arena、thread cache、size class、madvise 返还策略、碎片与 RSS 膨胀、如何根据负载选分配器。