资讯中心

内核定时器与高精度定时器(hrtimer)的实现原理

📅 2026/7/15 23:09:39
内核定时器与高精度定时器(hrtimer)的实现原理
内核定时器与高精度定时器(hrtimer)的实现原理一、Linux定时器体系的演进Linux内核有四种定时器机制。jiffies定时器最古老精度为tick级别1ms-10ms。timer_list基于jiffies用于大多数驱动。hrtimer基于ktime精度达纳秒级。POSIX timer面向用户空间。每次升级都是为了更高的精度和更低的开销。传统timer_list的问题在于精度受限。tick是周期性时钟中断。配置CONFIG_HZ250时tick间隔为4ms。这意味着1ms的定时需求实际延迟为2-4ms。这对实时应用是不可接受的。hrtimer使用硬件时钟事件设备绕过tick限制。精度取决于硬件能力通常可达纳秒级。graph TD subgraph 传统定时器 A[tick中断] -- B[jiffies递增] B -- C[遍历timer_list] C -- D[执行到期定时器] end subgraph 高精度定时器 E[红黑树到期时间] -- F[计算最近到期值] F -- G[编程clockevent设备] G -- H[硬件中断触发] H -- I[执行hrtimer回调] end D -- J[精度: ms级] I -- K[精度: ns级]二、定时器子系统核心数据结构2.1 timer_list与timer_base// include/linux/timer.h struct timer_list { struct hlist_node entry; unsigned long expires; /* 到期jiffies值 */ void (*function)(struct timer_list *); u32 flags; }; // kernel/time/timer.c struct timer_base { raw_spinlock_t lock; struct timer_list *running_timer; unsigned long clk; /* 当前jiffies */ unsigned long next_expiry; unsigned int cpu; bool is_idle; DECLARE_BITMAP(pending_map, WHEEL_SIZE); struct hlist_head vectors[WHEEL_SIZE]; } ____cacheline_aligned;timer_base使用时间轮Timer Wheel数据结构。将定时器按到期时间分散到不同槽位。查找复杂度O(1)。时间轮分为9个级别。第一级256个槽每个槽1个jiffy。第九级256个槽每个槽2^24个jiffies。这是经典的哈希分层时间轮。2.2 时间轮的级联展开/* 时间轮级联迁移的核心逻辑 */ static int __collect_expired_timers(struct timer_base *base, struct hlist_head *heads) { unsigned long clk base-clk; struct hlist_head *vec; int i, levels 0; unsigned int idx; /* 遍历所有到期的槽位 */ for (i 0; i LVL_DEPTH; i) { idx (clk LVL_MASK) i * LVL_SIZE; if (__test_and_clear_bit(idx, base-pending_map)) { vec base-vectors idx; hlist_move_list(vec, heads); levels; } /* 检查是否需要级联 */ if (clk LVL_CLK_MASK) break; /* 向下一个级别级联 */ clk LVL_CLK_SHIFT; } return levels; }三、hrtimer的设计与红黑树实现3.1 hrtimer数据结构hrtimer的核心是用红黑树替代时间轮。红黑树按到期时间排序。最小到期时间的节点在红黑树的最左端。查找下一个到期事件只需取最左节点。// include/linux/hrtimer.h struct hrtimer { struct timerqueue_node node; ktime_t _softexpires; enum hrtimer_restart (*function)(struct hrtimer *); struct hrtimer_clock_base *base; u8 state; u8 is_rel; u8 is_soft; u8 is_hard; }; // kernel/time/hrtimer.c struct hrtimer_cpu_base { raw_spinlock_t lock; unsigned int cpu; unsigned int active_bases; unsigned int clock_was_set_seq; unsigned int hres_active : 1; unsigned int in_hrtirq : 1; ktime_t expires_next; struct hrtimer *next_timer; unsigned int nr_events; unsigned int nr_retries; struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES]; }; /* 插入定时器到红黑树 */ static void enqueue_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base, enum hrtimer_mode mode) { /* 将定时器节点插入timerqueue红黑树 */ timerqueue_add(base-active, timer-node); /* 更新最左节点的到期时间 */ if (timer-node base-active.next) { base-cpu_base-active_bases | (1 base-index); hrtimer_update_next_event(base-cpu_base); } }3.2 高精度模式的激活系统在满足条件时可切换到高精度模式。需要硬件clockevent设备支持oneshot模式。中断延迟足够低。hrtimer子系统接管时钟中断。/* 切换到高精度定时器模式 */ static int hrtimer_switch_to_hres(void) { struct hrtimer_cpu_base *base this_cpu_ptr(hrtimer_bases); if (base-hres_active) return 1; /* 接管tick设备 */ tick_setup_sched_timer(); base-hres_active 1; /* 重新编程clockevent设备为oneshot模式 */ hrtimer_force_reprogram(base, 0); return 1; }3.3 到期处理与再编程sequenceDiagram participant HW as 硬件定时器 participant ISR as hrtimer_interrupt participant RB as 红黑树 participant CB as 回调函数 HW-ISR: 时钟中断触发 ISR-RB: 取最左到期节点 RB--ISR: timer_node loop 有到期定时器 ISR-RB: 移除节点 ISR-CB: 执行callback CB--ISR: HRTIMER_RESTART/NORESTART alt 需要重启 ISR-RB: 重新插入(新到期时间) end ISR-RB: 取下一个到期节点 end ISR-HW: 编程下一次到期时间/* hrtimer中断处理的核心函数 */ void hrtimer_interrupt(struct clock_event_device *dev) { struct hrtimer_cpu_base *cpu_base this_cpu_ptr(hrtimer_bases); ktime_t expires_next, now; int retries 0; now hrtimer_update_base(cpu_base); cpu_base-nr_events; __hrtimer_peek_ahead_timers(); for (int i 0; i HRTIMER_MAX_CLOCK_BASES; i) { struct hrtimer_clock_base *base; struct timerqueue_node *node; ktime_t basenow; base cpu_base-clock_base i; basenow ktime_add(now, base-offset); /* 遍历所有到期的定时器 */ while ((node timerqueue_getnext(base-active))) { struct hrtimer *timer; timer container_of(node, struct hrtimer, node); /* 检查是否真正到期 */ if (basenow hrtimer_get_expires_tv64(timer)) break; /* 从红黑树中移除 */ __remove_hrtimer(timer, base, HRTIMER_STATE_INACTIVE, 0); /* 执行回调 */ if (timer-function(timer) ! HRTIMER_RESTART) continue; /* 需要重启的重新入队 */ enqueue_hrtimer(timer, base, HRTIMER_MODE_PINNED); } } /* 重新编程下一次中断 */ expires_next __hrtimer_get_next_event(cpu_base); if (expires_next ! KTIME_MAX) { tick_program_event(expires_next, 1); } }四、使用示例与最佳实践4.1 驱动中使用timer_list#include linux/timer.h #include linux/module.h struct my_device { struct timer_list poll_timer; unsigned long poll_interval_ms; /* ...其他字段... */ }; static void poll_timer_callback(struct timer_list *t) { struct my_device *dev from_timer(dev, t, poll_timer); /* 执行轮询逻辑 */ dev_check_status(dev); /* 重新设置定时器 */ mod_timer(dev-poll_timer, jiffies msecs_to_jiffies(dev-poll_interval_ms)); } static int my_drv_probe(struct platform_device *pdev) { struct my_device *dev; dev devm_kzalloc(pdev-dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev-poll_interval_ms 500; /* 初始化并启动定时器 */ timer_setup(dev-poll_timer, poll_timer_callback, 0); mod_timer(dev-poll_timer, jiffies msecs_to_jiffies(dev-poll_interval_ms)); return 0; } static int my_drv_remove(struct platform_device *pdev) { struct my_device *dev platform_get_drvdata(pdev); /* 同步删除定时器 */ del_timer_sync(dev-poll_timer); return 0; }4.2 高精度定时器的使用#include linux/hrtimer.h struct high_precision_poller { struct hrtimer hr_timer; ktime_t period; }; static enum hrtimer_restart hr_poll_callback(struct hrtimer *timer) { struct high_precision_poller *poller container_of(timer, struct high_precision_poller, hr_timer); /* 高精度轮询逻辑 */ hrtimer_forward_now(timer, poller-period); return HRTIMER_RESTART; } void setup_high_precision_timer(struct high_precision_poller *poller, u64 interval_ns) { poller-period ns_to_ktime(interval_ns); hrtimer_init(poller-hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); poller-hr_timer.function hr_poll_callback; hrtimer_start(poller-hr_timer, poller-period, HRTIMER_MODE_REL); } void cleanup_timer(struct high_precision_poller *poller) { hrtimer_cancel(poller-hr_timer); }五、性能陷阱与调优5.1 常见问题问题原因解决办法定时器精度不足使用timer_list改用hrtimer回调执行时间长在软中断上下文做重活使用workqueue延迟大量定时器导致CPU load高遍历开销大使用时间轮合并hrtimer精度漂移系统负载波动使用CLOCK_MONOTONIC_RAW# 查看定时器统计 cat /proc/timer_list # 查看hrtimer详细信息 cat /proc/timer_list | grep -A10 hrtimer # 内核tracepoint追踪定时器 echo 1 /sys/kernel/debug/tracing/events/timer/timer_start/enable echo 1 /sys/kernel/debug/tracing/events/timer/hrtimer_start/enable cat /sys/kernel/debug/tracing/trace_pipe总结深入Linux内核定时器体系的实现原理。对比了timer_list基于jiffies的时间轮机制与hrtimer基于ktime的红黑树机制。timer_list使用9级哈希时间轮实现O(1)插入精度受限于tick间隔。hrtimer用红黑树替代时间轮通过clockevent设备的oneshot模式实现纳秒级精度。给出hrtimer_interrupt完整中断处理流程包括到期遍历、回调执行、重启入队和下一次中断编程。提供驱动开发中的timer_list和hrtimer使用模板。归纳了精度不足、回调阻塞、CPU负载和精度漂移四大陷阱及对策。