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。
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 的结构体中获取。