Linux内核定时器全面介绍
Linux内核提供了多种定时器机制,用于实现延迟执行、周期性任务和超时处理。不同的定时器适用于不同的场景,从纳秒级精度到秒级延迟,从软中断上下文到进程上下文。
一、内核定时器分类
| 定时器类型 | 精度 | 上下文 | 适用场景 |
|---|---|---|---|
| 低分辨率定时器 | 毫秒级(HZ依赖) | 软中断 | 非精确延迟、超时处理 |
| 高分辨率定时器 | 纳秒级 | 软中断 | 精确时间控制、多媒体 |
| 延迟函数 | 毫秒/微秒 | 进程上下文 | 短期延迟、忙等待 |
| 动态定时器 | 毫秒级 | 软中断 | 动态创建的单次定时器 |
| 工作队列 | 秒级 | 进程上下文 | 可睡眠的延迟工作 |
| 时钟源 | 硬件依赖 | 各种 | 系统时间管理 |
二、低分辨率定时器(Timer Wheel)
2.1 数据结构
c
struct timer_list {
struct hlist_node entry; // 哈希链表节点
unsigned long expires; // 期望的过期时间(jiffies值)
void (*function)(struct timer_list *); // 回调函数
u32 flags; // 标志位
void *start_site; // 调试用
unsigned int start_comm[16]; // 调试用
};
2.2 核心API
c
// 1. 初始化定时器
struct timer_list my_timer;
timer_setup(&my_timer, my_timer_callback, 0); // 现代方式
// 或
init_timer(&my_timer);
my_timer.function = my_timer_callback;
// 2. 添加/修改定时器
void add_timer(struct timer_list *timer); // 添加定时器
int mod_timer(struct timer_list *timer, unsigned long expires); // 修改过期时间
int timer_reduce(struct timer_list *timer, unsigned long expires); // 只允许减小过期时间
// 3. 删除定时器
int del_timer(struct timer_list *timer); // 删除定时器
int del_timer_sync(struct timer_list *timer); // 同步删除(等待回调完成)
// 4. 检查状态
int timer_pending(const struct timer_list *timer); // 定时器是否等待处理
int timer_active(struct timer_list *timer); // 定时器是否激活
2.3 完整示例
c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
static struct timer_list my_timer;
static int counter = 0;
// 定时器回调函数
static void my_timer_callback(struct timer_list *t)
{
counter++;
printk(KERN_INFO "Timer expired, counter = %d, jiffies = %lu\n",
counter, jiffies);
// 重新启动定时器(实现周期性)
mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000)); // 1秒后再次触发
}
static int __init timer_init(void)
{
printk(KERN_INFO "Timer module init\n");
// 初始化定时器
timer_setup(&my_timer, my_timer_callback, 0);
// 设置定时器:1秒后触发
my_timer.expires = jiffies + msecs_to_jiffies(1000);
// 添加定时器
add_timer(&my_timer);
return 0;
}
static void __exit timer_exit(void)
{
// 删除定时器
del_timer_sync(&my_timer);
printk(KERN_INFO "Timer module exit\n");
}
module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
2.4 定时器标志
c
// 定时器标志位
#define TIMER_DEFERRABLE 0x0001 // 可延迟,不影响系统唤醒
#define TIMER_IRQSAFE 0x0002 // 中断安全
#define TIMER_PINNED 0x0004 // 绑定到特定CPU
#define TIMER_ARRAYSHIFT 8
三、高分辨率定时器(hrtimer)
高分辨率定时器使用 ktime_t 表示纳秒级时间,精度可达硬件时钟精度(通常1ns)。
3.1 数据结构
c
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; // 是否相对时间
};
3.2 核心API
c
// 1. 初始化
void hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
enum hrtimer_mode mode);
// clock_id: CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_BOOTTIME
// mode: HRTIMER_MODE_ABS, HRTIMER_MODE_REL, HRTIMER_MODE_PINNED
// 2. 启动定时器
int hrtimer_start(struct hrtimer *timer, ktime_t time,
const enum hrtimer_mode mode);
// 3. 取消定时器
int hrtimer_cancel(struct hrtimer *timer);
int hrtimer_try_to_cancel(struct hrtimer *timer);
// 4. 修改定时器
int hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval);
// 5. 检查状态
int hrtimer_active(const struct hrtimer *timer);
3.3 高精度定时器示例
c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
static struct hrtimer hr_timer;
static ktime_t period;
// 高精度定时器回调
static enum hrtimer_restart hr_timer_callback(struct hrtimer *timer)
{
static int count = 0;
ktime_t now;
count++;
now = ktime_get();
printk(KERN_INFO "High-res timer expired: count=%d, time=%lld ns\n",
count, ktime_to_ns(now));
if (count < 10) {
// 重新启动,设置相对时间
hrtimer_forward(timer, now, period);
return HRTIMER_RESTART;
}
return HRTIMER_NORESTART;
}
static int __init hrtimer_init_module(void)
{
printk(KERN_INFO "High-res timer module init\n");
// 初始化高精度定时器(单调时钟)
hrtimer_init(&hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
hr_timer.function = hr_timer_callback;
// 设置周期为10毫秒
period = ktime_set(0, 10 * 1000000); // 10ms = 10,000,000 ns
// 启动定时器
hrtimer_start(&hr_timer, period, HRTIMER_MODE_REL);
return 0;
}
static void __exit hrtimer_exit_module(void)
{
hrtimer_cancel(&hr_timer);
printk(KERN_INFO "High-res timer module exit\n");
}
module_init(hrtimer_init_module);
module_exit(hrtimer_exit_module);
MODULE_LICENSE("GPL");
四、延迟函数(Busy Waiting)
4.1 长延迟(进程上下文)
c
// 秒级延迟
void msleep(unsigned int msecs); // 毫秒延迟,不可中断
void msleep_interruptible(unsigned int msecs); // 可被信号中断
void ssleep(unsigned int seconds); // 秒延迟
// 示例
msleep(100); // 延迟100毫秒
ssleep(5); // 延迟5秒
if (msleep_interruptible(2000)) // 延迟2秒,可中断
printk("Interrupted!\n");
4.2 短延迟(原子上下文)
c
#include <linux/delay.h>
// 微秒级延迟
void udelay(unsigned long usecs); // 忙等待微秒
void ndelay(unsigned long nsecs); // 忙等待纳秒(有限制)
void mdelay(unsigned long msecs); // 忙等待毫秒(不推荐)
// 示例
udelay(100); // 延迟100微秒
ndelay(1000); // 延迟1微秒
mdelay(10); // 延迟10毫秒(会占用CPU)
4.3 延迟函数对比
| 函数 | 精度 | 上下文 | CPU占用 | 可中断 |
|---|---|---|---|---|
msleep() |
毫秒 | 进程 | 低(调度) | 否 |
msleep_interruptible() |
毫秒 | 进程 | 低 | 是 |
udelay() |
微秒 | 原子 | 高(忙等待) | 否 |
ndelay() |
纳秒 | 原子 | 高 | 否 |
schedule_timeout() |
自定义 | 进程 | 低 | 可配置 |
五、动态定时器(Timer Wheel进阶)
5.1 每CPU定时器
c
#include <linux/timer.h>
// 定义每CPU定时器
DEFINE_PER_CPU(struct timer_list, cpu_timer);
static void cpu_timer_callback(struct timer_list *t)
{
// 处理每CPU定时事件
printk(KERN_INFO "Timer on CPU %d\n", smp_processor_id());
}
static int init_cpu_timers(void)
{
int cpu;
for_each_possible_cpu(cpu) {
struct timer_list *timer = per_cpu_ptr(&cpu_timer, cpu);
timer_setup(timer, cpu_timer_callback, 0);
timer->expires = jiffies + HZ;
add_timer_on(timer, cpu); // 绑定到特定CPU
}
return 0;
}
5.2 可睡眠定时器
c
#include <linux/timer.h>
#include <linux/workqueue.h>
struct delayed_work {
struct work_struct work;
struct timer_list timer;
};
// 使用工作队列的可延迟工作
static struct delayed_work my_delayed_work;
static void delayed_work_func(struct work_struct *work)
{
// 在进程上下文中执行,可以睡眠
printk(KERN_INFO "Delayed work executed\n");
msleep(100); // 可以睡眠
}
static int schedule_delayed_work(void)
{
INIT_DELAYED_WORK(&my_delayed_work, delayed_work_func);
// 5秒后执行
schedule_delayed_work(&my_delayed_work, msecs_to_jiffies(5000));
return 0;
}
六、内核时间管理基础
6.1 jiffies和HZ
c
#include <linux/jiffies.h>
// HZ:每秒的时钟中断次数(通常100/250/1000)
// jiffies:自启动以来的时钟中断次数
unsigned long jiffies; // 全局变量,当前时间
unsigned long jiffies_64; // 64位版本
// 时间转换
unsigned long j = msecs_to_jiffies(1000); // 毫秒转jiffies
unsigned long j = usecs_to_jiffies(1000); // 微秒转jiffies
unsigned int ms = jiffies_to_msecs(j); // jiffies转毫秒
// 时间比较(避免回绕问题)
time_after(a, b); // a > b
time_before(a, b); // a < b
time_after_eq(a, b); // a >= b
time_before_eq(a, b);// a <= b
// 示例
unsigned long timeout = jiffies + HZ; // 1秒后
while (time_before(jiffies, timeout)) {
// 等待1秒
cpu_relax();
}
6.2 ktime_t(高精度时间)
c
#include <linux/ktime.h>
// 创建时间
ktime_t kt = ktime_set(1, 500000000); // 1.5秒
ktime_t kt = ktime_get(); // 当前时间(单调)
ktime_t kt = ktime_get_real(); // 真实时间
ktime_t kt = ktime_get_boottime(); // 启动时间(含休眠)
ktime_t kt = ns_to_ktime(1000000000); // 纳秒转换
// 时间运算
ktime_t a = ktime_set(0, 1000000); // 1毫秒
ktime_t b = ktime_set(0, 2000000); // 2毫秒
ktime_t sum = ktime_add(a, b); // 相加
ktime_t sub = ktime_sub(b, a); // 相减
bool cmp = ktime_compare(a, b) < 0; // 比较
// 获取差值
s64 diff = ktime_to_ns(ktime_sub(b, a)); // 纳秒差值
七、实际应用场景
7.1 网络协议栈超时处理
c
// TCP重传定时器
struct tcp_sock {
struct timer_list retransmit_timer;
// ...
};
static void tcp_retransmit_timer(struct timer_list *t)
{
struct tcp_sock *tp = from_timer(tp, t, retransmit_timer);
// 检查是否超时
if (tcp_write_queue_empty(tp))
return;
// 执行重传
tcp_retransmit_skb(tp, tcp_write_queue_head(tp));
// 重新设置定时器
mod_timer(&tp->retransmit_timer,
jiffies + tcp_timeout_recalc(tp));
}
7.2 驱动程序轮询
c
// 网络驱动NAPI轮询定时器
struct my_priv {
struct timer_list poll_timer;
struct net_device *dev;
};
static void my_poll_timer(struct timer_list *t)
{
struct my_priv *priv = from_timer(priv, t, poll_timer);
// 检查硬件状态
if (check_hardware_status(priv)) {
// 重新调度轮询
mod_timer(&priv->poll_timer, jiffies + HZ / 100); // 10ms
}
}
static int my_driver_init(struct net_device *dev)
{
struct my_priv *priv = netdev_priv(dev);
timer_setup(&priv->poll_timer, my_poll_timer, 0);
priv->poll_timer.expires = jiffies + HZ / 100;
add_timer(&priv->poll_timer);
return 0;
}
7.3 内核统计采样
c
// 每5秒采集一次系统统计
static struct timer_list stats_timer;
static void collect_stats(struct timer_list *t)
{
int cpu;
// 采集每CPU统计
for_each_online_cpu(cpu) {
struct cpu_stats *stats = per_cpu_ptr(&cpu_stats, cpu);
// 收集统计...
}
// 重新调度
mod_timer(&stats_timer, jiffies + 5 * HZ);
}
static int __init init_stats_collector(void)
{
timer_setup(&stats_timer, collect_stats, 0);
stats_timer.expires = jiffies + 5 * HZ;
add_timer(&stats_timer);
return 0;
}
八、性能优化与注意事项
8.1 定时器精度选择
c
// 根据需求选择合适的精度
if (need_ns_precision) {
// 使用高精度定时器
struct hrtimer hrt;
hrtimer_init(&hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
hrtimer_start(&hrt, ns_to_ktime(1000000), HRTIMER_MODE_REL);
} else {
// 使用低精度定时器
struct timer_list timer;
timer_setup(&timer, callback, 0);
mod_timer(&timer, jiffies + msecs_to_jiffies(1));
}
8.2 避免定时器漂移
c
// 错误:累积误差
static void bad_timer_callback(struct timer_list *t)
{
// 每次重新调度会累积执行时间误差
mod_timer(t, jiffies + HZ); // 固定延迟
}
// 正确:修正误差
static void good_timer_callback(struct timer_list *t)
{
static unsigned long next = 0;
if (next == 0)
next = jiffies + HZ;
else
next += HZ; // 基于预期时间
mod_timer(t, next); // 基于绝对时间
}
8.3 同步删除定时器
c
// 错误:可能在回调中删除自己导致死锁
static void self_del_timer(struct timer_list *t)
{
del_timer(t); // 错误!可能死锁
}
// 正确:使用同步删除
static void safe_self_del_timer(struct timer_list *t)
{
// 让定时器不再重启即可,不需要主动删除
}
// 模块卸载时必须同步删除
static void __exit module_exit(void)
{
del_timer_sync(&my_timer); // 等待回调完成
}
8.4 定时器回调函数约束
c
static void timer_callback(struct timer_list *t)
{
// 在软中断上下文中执行,注意限制:
// 1. 不能睡眠
// msleep(100); // 错误!会导致内核崩溃
// 2. 不能访问用户空间
// copy_to_user(...); // 错误!
// 3. 避免长时间操作
// for (i = 0; i < 1000000; i++) // 不推荐
// 4. 使用spinlock而非mutex
spin_lock(&my_lock); // 可以
// mutex_lock(&my_mutex); // 错误!
// 5. 如果需要睡眠操作,使用工作队列
schedule_work(&my_work);
}
九、调试技巧
9.1 定时器跟踪
c
// 查看所有定时器
cat /proc/timer_list
// 查看定时器统计
cat /proc/timer_stats
// 启用定时器统计
echo 1 > /proc/timer_stats
9.2 调试打印
c
// 打印定时器信息
void dump_timer_info(struct timer_list *timer)
{
printk(KERN_INFO "Timer: function=%pS, expires=%lu, current=%lu\n",
timer->function, timer->expires, jiffies);
if (time_before(timer->expires, jiffies))
printk(KERN_WARNING "Timer already expired!\n");
}
十、总结
Linux内核定时器提供了丰富的选择:
- 低分辨率定时器(timer_list):适用于毫秒级、非精确的延迟需求
- 高分辨率定时器(hrtimer):需要纳秒级精度时使用
- 延迟函数:简单场景下的短期延迟
- 工作队列定时器:需要睡眠操作时使用
- 每CPU定时器:减少锁竞争,提高性能
选择建议:
- 网络超时、设备轮询 →
timer_list - 多媒体、实时控制 →
hrtimer - 驱动短延迟 →
udelay/mdelay - 可睡眠延迟操作 →
delayed_work
在实际开发中,应根据精度要求、上下文限制和性能需求选择合适的定时器机制。