1. Linux内核定时器概述

内核定时器是Linux内核中实现时间驱动行为的基础设施。驱动模块通过定时器在未来的某个时刻执行指定的回调函数,而无需占用CPU进行忙等待。理解内核定时器的工作原理,是掌握内核延时调度、超时处理、周期性任务等机制的前提。

1.1 定时器在内核中的作用

内核定时器在驱动开发中承担以下核心职责:

  • 超时处理:等待硬件操作完成时设置超时,避免无限阻塞。
  • 周期性任务:以固定间隔轮询设备状态或更新软件计数器。
  • 延时执行:将非紧急任务推迟到后续时间片执行,降低中断上下文负载。
  • 协议时序控制:在通信协议中精确控制信号时序(如I²C、SPI的片选保持时间)。
  • 动态节流:根据系统负载动态调整采样频率或电源管理策略。
关键认知:内核定时器的回调函数在软中断上下文中执行,不能调用可能引起睡眠的函数(如 copy_from_user、kmalloc(GFP_KERNEL))。这是驱动开发者最容易踩入的陷阱之一。

1.2 内核定时器 vs 用户空间定时器

用户空间定时器(如 timerfd、setitimer、POSIX timer_create)与内核定时器在精度、上下文、开销和适用场景上存在本质差异。下表从多个维度进行对比:

对比维度 内核定时器 用户空间定时器
执行上下文 软中断(原子上下文) 进程上下文(可睡眠)
精度 取决于内核HZ值(通常1~10ms) 受调度器影响,通常ms级
最小粒度 1个jiffy(约1~10ms) 受内核调度周期限制
开销 极低(无系统调用切换) 较高(涉及系统调用、信号传递)
可睡眠 ❌ 不允许 ✅ 允许
适用场景 驱动超时、硬件轮询、协议时序 应用层心跳、超时重试、UI刷新
编程接口 timer_list / hrtimer timerfd / setitimer
常见误区:初学者常试图在内核定时器回调中调用 msleep 或 ssleep,这会导致内核崩溃。内核定时器回调必须使用 udelay、ndelay 等忙等待函数,或通过工作队列将任务推迟到进程上下文。

1.3 内核时间管理基础

要正确使用内核定时器,必须先理解内核的时间管理基石——jiffies 和 HZ。

1.3.1 jiffies 与 HZ

jiffies 是内核全局的时钟滴答计数器,每次时钟中断递增1。HZ 是每秒的时钟中断次数,由内核编译时配置决定:

  • HZ=100:每个jiffy = 10ms(常用于服务器,降低中断开销)
  • HZ=250:每个jiffy = 4ms(桌面系统默认值)
  • HZ=1000:每个jiffy = 1ms(实时性要求高的嵌入式系统)

内核提供以下辅助宏进行时间单位转换:

#include <linux/jiffies.h>

// 毫秒 → jiffies
unsigned long msecs_to_jiffies(const unsigned int m);

// jiffies → 毫秒
unsigned int jiffies_to_msecs(const unsigned long j);

// 微秒 → jiffies
unsigned long usecs_to_jiffies(const unsigned int u);

// 判断时间是否已超时
int time_after(unsigned long a, unsigned long b);
int time_before(unsigned long a, unsigned long b);

1.3.2 高精度定时器(hrtimer)

对于需要微秒级精度的场景,内核提供了高精度定时器(hrtimer),它基于硬件定时器(如TSC、HPET)实现,不依赖jiffies滴答:

  • 精度可达纳秒级(取决于硬件能力)
  • 使用 ktime_t 表示时间
  • 回调同样在软中断上下文执行
选择指南:普通驱动超时(几十ms以上)使用 timer_list + jiffies;需要高精度(μs级)或短周期(<10ms)使用 hrtimer。
内核时间管理架构 硬件定时器层 PIT / HPET / TSC / ARM Generic Timer 内核时间管理核心 时钟中断处理 → jiffies递增 → 定时器链表扫描 HZ=100/250/1000 | jiffies | time_after/before 定时器接口层 timer_list(低精度) 基于jiffies | 最小粒度1/HZ秒 hrtimer(高精度) 基于硬件时钟 | 纳秒级精度 驱动开发者主要与 timer_list 和 hrtimer 接口交互

1.4 核心数据结构:timer_list

内核定时器的核心数据结构定义在 <linux/timer.h> 中:

struct timer_list {
    struct hlist_node   entry;      // 内核链表节点
    unsigned long       expires;    // 到期时间(jiffies值)
    void (*function)(struct timer_list *); // 回调函数
    unsigned long       flags;      // 标志位
    int                 start_pid;  // 调试用
    void                *private;   // 私有数据
};

关键字段说明:

  • expires:定时器到期时的jiffies值。设置时使用 jiffies + delay 形式。
  • function:到期时执行的回调函数,参数为 struct timer_list * 指针。
  • flags:控制定时器行为,如 TIMER_IRQSAFE(在中断上下文安全执行)。
编程要点:回调函数原型固定为 void callback(struct timer_list *t)。如果需要传递额外数据,可以使用 container_of 宏从包含该 timer_list 的结构体中获取。
timer_list 数据结构与回调关系 struct timer_list entry: hlist_node expires: unsigned long function: callback flags: unsigned long private: void * 回调函数 void callback(struct timer_list *t) 在软中断上下文执行 通过 container_of 可从 timer_list 指针获取包含它的设备结构体

1.5 本章小结