3. hrtimer核心数据结构

高精度定时器(hrtimer)的整个运行机制建立在三层核心数据结构之上。理解这三层结构之间的层级关系与字段含义,是编写可靠实时周期任务的基石。本章从最底层的定时器对象开始,逐层向上剖析,最终构建出完整的 per-CPU 定时器管理视图。

3.1 三层结构总览

hrtimer 子系统采用 “对象 → 时钟基 → CPU 上下文” 的三层架构。每个定时器对象(struct hrtimer)归属于一个时钟基(struct hrtimer_clock_base),而所有时钟基又统一由 per-CPU 的管理结构(struct hrtimer_cpu_base)组织。

设计哲学: 将定时器按时钟源分类管理,利用红黑树实现 O(log n) 的插入与删除,并通过 per-CPU 结构避免锁竞争,满足实时系统对确定性的严苛要求。
hrtimer 三层核心数据结构关系图 struct hrtimer_cpu_base per-CPU 定时器上下文 clock_base[] 数组(最多 4 个时钟基) CLOCK_MONOTONIC index=0 CLOCK_REALTIME index=1 CLOCK_BOOTTIME index=2 CLOCK_TAI index=3 每个 clock_base 维护一棵红黑树(struct rb_root_cached) struct hrtimer struct hrtimer struct hrtimer struct hrtimer 每个 CPU 拥有独立的 hrtimer_cpu_base,实现无锁本地定时器操作

3.2 struct hrtimer:定时器对象

struct hrtimer 是驱动开发者直接操作的核心结构体。每个定时器实例代表一个即将到期的事件。

struct hrtimer {
    struct timerqueue_node      node;       /* 红黑树节点,包含 expire 时间 */
    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;    /* 是否为硬中断上下文 */
};
字段说明实时编程要点
node红黑树节点,内含 expires 字段(绝对到期时间)内核通过该节点将定时器插入时钟基的红黑树
_softexpires软到期时间,允许回调在 expires 之后触发用于合并中断,减少唤醒次数;实时任务建议与 expires 一致
function定时器到期时执行的回调函数运行在中断上下文,必须快速、不可睡眠、不可持有锁
base指向所属的 hrtimer_clock_base内核通过该指针定位时钟基和红黑树
state定时器状态(HRTIMER_STATE_INACTIVE / ENQUEUED / CALLBACK 等)检查状态可判断定时器是否正在运行
关键约束: 回调函数 function 在 硬中断上下文 或 软中断上下文 中执行。禁止调用可能导致睡眠的 API(如 kmalloc(GFP_KERNEL)、mutex_lock())。回调的返回值决定定时器是否重新启动。

3.3 struct hrtimer_clock_base:时钟基管理

每个时钟基管理同一时钟源下的所有定时器。内核支持 4 种时钟基,分别对应不同的时间语义。

struct hrtimer_clock_base {
    struct hrtimer_cpu_base  *cpu_base;     /* 所属 CPU 上下文 */
    struct timerqueue_head   active;        /* 红黑树根节点(含缓存的最左节点) */
    ktime_t                  (*get_time)(void); /* 获取当前时间的函数指针 */
    ktime_t                  offset;        /* 时钟偏移量(如 REALTIME 与 MONOTONIC 的差值) */
    clockid_t                clockid;       /* 时钟 ID */
    u8                       index;         /* 在 cpu_base->clock_base[] 中的索引 */
};
时钟基类型clockidindex特点
CLOCK_MONOTONIC10单调递增,不受系统时间跳变影响;实时任务首选
CLOCK_REALTIME01墙上时钟,可能因 NTP 或用户调整而跳变
CLOCK_BOOTTIME72类似 MONOTONIC,但包含系统挂起时间
CLOCK_TAI113国际原子时,用于精确时间同步协议
实时周期任务建议: 始终使用 CLOCK_MONOTONIC。它不受 NTP 时间调整影响,能保证定时器间隔的确定性。如果任务需要在系统挂起时继续计时,则选择 CLOCK_BOOTTIME。

3.4 struct hrtimer_cpu_base:Per-CPU 定时器上下文

每个 CPU 核心拥有一个独立的 hrtimer_cpu_base 实例,集中管理该 CPU 上的所有时钟基和定时器。这种 per-CPU 设计避免了多核间的锁竞争,是 hrtimer 高性能的关键。

struct hrtimer_cpu_base {
    raw_spinlock_t               lock;           /* 保护本结构体的自旋锁 */
    struct hrtimer_clock_base    clock_base[HRTIMER_MAX_CLOCK_BASES]; /* 时钟基数组 */
    unsigned int                 active_bases;   /* 位掩码,标记哪些时钟基非空 */
    unsigned int                 nr_events;      /* 定时器到期事件计数 */
    unsigned int                 nr_retries;     /* 重试次数统计 */
    unsigned int                 nr_hangs;       /* 挂起次数统计 */
    ktime_t                      expires_next;   /* 下一个到期时间(用于编程硬件定时器) */
    struct hrtimer               *next_timer;    /* 指向下一个到期的定时器 */
    ktime_t                      softirq_expires_next; /* 软中断下一个到期时间 */
    struct hrtimer               *softirq_next_timer;  /* 软中断下一个到期定时器 */
};
struct hrtimer_cpu_base 内部结构(单 CPU 视角) struct hrtimer_cpu_base (CPU 0) raw_spinlock_t lock nr_events / nr_retries next_timer / expires_next clock_base[HRTIMER_MAX_CLOCK_BASES] (4 个时钟基) index=0 MONOTONIC active: rb_root get_time: ktime_get offset: 0 clockid: 1 index=1 REALTIME active: rb_root get_time: ktime_get_real offset: TAI-UTC clockid: 0 index=2 BOOTTIME active: rb_root get_time: ktime_get_boottime offset: 0 clockid: 7 index=3 TAI active: rb_root get_time: ktime_get_clocktai offset: 0 clockid: 11 active_bases 位掩码标记非空时钟基,避免遍历所有时钟基

3.5 关键设计要点

  • 红黑树组织: 每个时钟基的 active 字段是一个 struct timerqueue_head,它封装了红黑树根节点并缓存最左节点(即最早到期的定时器),实现 O(1) 获取下一个到期定时器。
  • Per-CPU 无锁设计: 每个 CPU 操作自己的 hrtimer_cpu_base,仅在迁移定时器时涉及跨 CPU 锁。实时任务应绑定 CPU 以避免迁移开销。
  • 软到期与硬到期: _softexpires 允许内核将多个定时器合并到同一个硬件中断,减少中断频率。对于需要严格周期性的实时任务,应将 _softexpires 设置为与 expires 相同。
  • 状态机转换: 定时器状态在 INACTIVE → ENQUEUED → CALLBACK → INACTIVE 之间转换。回调函数返回 HRTIMER_RESTART 时,内核自动重新将定时器插入红黑树。
驱动开发经验: 在编写 hrtimer 回调时,通过 hrtimer_cb_get_time() 获取当前时间,计算与 hrtimer->node.expires 的差值,可以精确测量调度延迟(jitter)。这是评估实时系统性能的常用手段。

3.6 数据结构关系总结

层级结构体职责关键字段
对象层struct hrtimer表示一个定时器事件node, function, base, state
时钟基管理层struct hrtimer_clock_base按时钟源分类管理定时器active, get_time, offset, clockid
CPU 上下文层struct hrtimer_cpu_baseper-CPU 全局定时器状态lock, clock_base[], expires_next