内核定时器

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内核定时器提供了丰富的选择:

  1. 低分辨率定时器(timer_list):适用于毫秒级、非精确的延迟需求
  2. 高分辨率定时器(hrtimer):需要纳秒级精度时使用
  3. 延迟函数:简单场景下的短期延迟
  4. 工作队列定时器:需要睡眠操作时使用
  5. 每CPU定时器:减少锁竞争,提高性能

选择建议:

  • 网络超时、设备轮询 → timer_list
  • 多媒体、实时控制 → hrtimer
  • 驱动短延迟 → udelay/mdelay
  • 可睡眠延迟操作 → delayed_work

在实际开发中,应根据精度要求、上下文限制和性能需求选择合适的定时器机制。

相关推荐
上天_去_做颗惺星 EVE_BLUE2 小时前
Linux Core Dump 测试操作手册
linux·c++·测试工具
拾贰_C2 小时前
【Ubuntu | 自动联网 | 网络问题】Ubuntu无法自动联网问题
linux·网络·ubuntu
0110编程之路2 小时前
Wine & Ubuntu 调用 Windows 应用
linux·windows·ubuntu
晨非辰2 小时前
Git版本控制速成:提交三板斧/日志透视/远程同步15分钟精通,掌握历史回溯与多人协作安全模型
linux·运维·服务器·c++·人工智能·git·后端
gdizcm2 小时前
linux判断文件类型的多种方法
linux·c++
云栖梦泽2 小时前
Linux内核与驱动:3.驱动模块传参,内核模块符号导出
linux·服务器·c++
程序猿编码2 小时前
网络数据包环形缓存捕获技术:原理、设计与实现(C/C++代码实现)
linux·c语言·网络·tcp/ip·缓存
默|笙2 小时前
【Linux】进程信号(4)_信号捕捉_内核态与用户态
linux·运维·服务器
supersolon2 小时前
PVE9安装32位爱快路由(ikuai)
linux·运维·网络