Linux RT 分析Linux 6.12.107

05 · 定时器:hard / soft 分流与 expiry_lock

结论:非 RT 内核所有 hrtimer 回调都在时钟中断的 hardirq 上下文执行,timer wheel 回调在 TIMER_SOFTIRQ 里执行;任何慢回调直接推迟所有任务。RT 内核在 hrtimer_init() 里把没有显式标记 HRTIMER_MODE_HARD 的 hrtimer 一律改成 soft 模式,在 HRTIMER_SOFTIRQ(线程上下文)里到期;只有调度 tick、RT/DL 任务的 sleeper 等保留 hard 模式。为了取消正在运行的回调时不自旋等一个可能被抢占的 softirq 线程,RT 给 hrtimer_cpu_basetimer_base 各加了一把 expiry_lockhrtimer_cancel()/timer_delete_sync() 在 RT 上会睡眠。

第一层

先看结论:RT 只让显式 HARD 的定时器留在 hardirq,其余全部进 softirq 线程;RT 任务自己的睡眠唤醒仍走 hardirq,精度不受影响。

非 RThrtimer 全在 hardirq,timer wheel 在 softirq
非 RT:hrtimer 全在 hardirq,timer wheel 在 softirq
同一场景 · 换成 PREEMPT_RT 内核
PREEMPT_RT只有 HARD 留在 hardirq,其余进线程
RT:只有 HARD 留在 hardirq,其余进线程

第二层

先看结论:分流发生在 hrtimer_init();取消正在运行的回调改为在 expiry_lock 上睡眠,避免自旋等一个被抢占的 softirq 线程。

非 RThrtimer_interrupt → __run_hrtimer;tick → TIMER_SOFTIRQ
非 RT:hrtimer_interrupt → __run_hrtimer;tick → TIMER_SOFTIRQ
同一场景 · 换成 PREEMPT_RT 内核
PREEMPT_RTsoft/hard 分流、expiry_lock、sleeper 自动 HARD
RT:soft/hard 分流、expiry_lock、sleeper 自动 HARD

中心规则

kernel/time/hrtimer.c:1616-1623

/*
 * On PREEMPT_RT enabled kernels hrtimers which are not explicitly
 * marked for hard interrupt expiry mode are moved into soft
 * interrupt context for latency reasons and because the callbacks
 * can invoke functions which might sleep on RT, e.g. spin_lock().
 */
if (IS_ENABLED(CONFIG_PREEMPT_RT) && !(mode & HRTIMER_MODE_HARD))
    softtimer = true;

soft base 是 clock base 数组的上半部分(:1637-1640)。hrtimer_start_range_ns() 在 RT 上检查的是 HRTIMER_MODE_HARDtimer->is_hard 的一致性而非 SOFT(:1332-1340)。6.12.107 只有 hrtimer_init():1657-1663),hrtimer_setup() 是后来的 API。

谁还留在 hardirq

到期路径

hrtimer_interrupt():1849-1879):soft 到期时间到了就 raise_softirq_irqoff(HRTIMER_SOFTIRQ):1873-1877),随后只跑 HRTIMER_ACTIVE_HARD:1879)。hrtimer_run_softirq():1824-1841)持 softirq_expiry_lockHRTIMER_ACTIVE_SOFT__hrtimer_run_queues() 每跑完一个 soft 回调调用 hrtimer_sync_wait_running():1817-1819),给等待取消的任务让路。

expiry_lock:取消时不再自旋

:1440-1455 的注释是 RT 定时器设计的核心:在 RT 上,hrtimer_try_to_cancel() 因回调正在运行而失败时,如果调用者在别的 CPU 自旋等待,会产生无界优先级反转;如果调用者就是抢占了回调的那个任务,自旋永远不会结束。所以 hrtimer_cancel_wait_running():1456-1480)对 soft 定时器改为 spin_lock_bh(&base->cpu_base->softirq_expiry_lock) 睡眠等待(享受 PI,把 softirq 线程提上来),hard 定时器仍 cpu_relax()

kernel/time/timer.c 完全同构:timer_base 增加 expiry_locktimer_waiters:254-256),del_timer_wait_running():1586-1607)睡眠等待(TIMER_IRQSAFE 定时器例外),__timer_delete_sync() 在 RT 上要求可抢占上下文(:1660-1665)——timer_delete_sync() 在 RT 上可能睡眠,不能在 raw 临界区调用。expire_timers():1839-1850)非 IRQSAFE 回调开中断执行并在每个回调后 timer_sync_wait_running()

6.12.107 没有的东西

ARM64:arch timer

性能影响

净影响:普通任务的定时器唤醒多一次线程切换;RT 任务不受影响;hrtimer_cancel/timer_delete_sync 在 RT 上可能睡眠。

指标 非 RT RT 说明
hrtimer 回调对最坏延迟的贡献 全部回调时间 仅 HARD 回调(都很短) 核心收益
RT 任务周期唤醒抖动 受其它 hrtimer 回调影响 不受普通回调影响(HARD sleeper) RT 上更稳
普通任务定时器唤醒延迟 hardirq 直接唤醒 多一次 softirq 线程切换(几 µs – 几十 µs,视负载) 例如 epoll_wait 超时、usleep
hrtimer_cancel / timer_delete_sync 自旋等待 可能睡眠 调用上下文限制更严
高频定时器负载 softirq 就地处理 ksoftirqd 更忙,可被 RT 任务压住 网络重传定时器、tickless 关闭时

源码定位

主题 位置
RT 强制 soft kernel/time/hrtimer.c:1609-1640
模式常量 include/linux/hrtimer.h:30-55
一致性检查 kernel/time/hrtimer.c:1332-1340
sleeper HARD 规则 kernel/time/hrtimer.c:2005-2044
到期分流 kernel/time/hrtimer.c:1785-1819, 1849-1879, 1824-1841, 2309
expiry_lock(RT) kernel/time/hrtimer.c:1392-1491include/linux/hrtimer_defs.h:111, 120-121
hrtimer_cancel kernel/time/hrtimer.c:1501-1512
timer wheel RT 字段与等待 kernel/time/timer.c:214-223, 254-256, 1541-1613, 1658-1674, 1839-1850, 2424-2457
POSIX CPU 定时器 kernel/time/posix-cpu-timers.c:1272-1300kernel/time/Kconfig:53-58arch/arm64/Kconfig:237
arm64 arch timer drivers/clocksource/arm_arch_timer.c:97-103, 252-256, 1251-1269, 1326

调优要点

小结