定时器和时间管理

jiffies

全局变量用于自系统启动以来产生的节拍总数


基础术语

  1. 硬件时钟 内核依赖底层硬件提供时间基准,主要有两种:

  1. 软件时钟(关键概念)内核利用硬件提供的时钟中断,在软件层面上维护了几个极其重要的概念:

硬件时钟跳动一次内核所发生的事

1.体系结构部分 由于系统架构的不同(arm,x86)其处理硬件时钟的中断的方式不同,所以内核会有针对其架构的中断代码,主要内容为:

2.体系结构无关部分


定时器怎么用

内核定时器的生命周期和固定 API 主要包含以下几个步骤:

1. 定义定时器结构体

首先,你需要定义一个 timer_list 结构体变量。

#include <linux/timer.h>

struct timer_list my_timer;

2. 初始化定时器:timer_setup()

在给定时器设定时间之前,必须先初始化它,把它和你的处理函数绑定起来。

void timer_setup(struct timer_list *timer,
                 void (*callback)(struct timer_list *),
                 unsigned int flags);

回调函数的参数直接就是这个定时器的指针。这比老版本更安全,你可以通过 from_timer 宏反向推导出包含这个定时器的外层结构体。


3. 设置超时时间并激活:add_timer()

初始化完成后,你需要告诉内核这个定时器什么时候触发,并把它加入到内核的时间轮中。

// 设置超时时间:当前时间 (jiffies) 加上你想等待的 Tick 数
// 比如等待 2 秒:jiffies + msecs_to_jiffies(2000)
my_timer.expires = jiffies + msecs_to_jiffies(2000); 

// 将定时器加入内核链表,正式激活
add_timer(&my_timer);

4. 修改定时器:mod_timer() (最常用的 API)

在实际开发中,mod_timer() 比 add_timer() 用得更频繁。它的作用是改变一个定时器的超时时间,并激活它。 哪怕这个定时器还没有被 add_timer() 激活过,或者它已经超时执行完了,你都可以直接用 mod_timer()。

// 把定时器的时间修改为从现在起 1 秒后触发
mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));

5. 删除定时器:del_timer() 与 del_timer_sync()

当你不再需要这个定时器(比如卸载模块或者关闭设备)时,必须把它从内核中删掉,否则内核去执行一个已经被释放的内存地址上的函数,会导致系统立刻崩溃(Kernel Panic)。


💡 完整的使用示例 (现代内核风格)

#include <linux/module.h>
#include <linux/timer.h>
#include <linux/jiffies.h>

// 1. 定义定时器
static struct timer_list my_timer;

// 定时器回调函数(注意参数是 timer_list 指针)
static void my_timer_callback(struct timer_list *timer)
{
    printk(KERN_INFO "定时器响了!当前 jiffies: %lu\n", jiffies);
    
    // 如果你想让它变成周期性定时器,可以在这里再次 mod_timer
    // mod_timer(timer, jiffies + msecs_to_jiffies(1000)); 
}

static int __init my_module_init(void)
{
    printk(KERN_INFO "模块加载...\n");

    // 2. 初始化定时器
    timer_setup(&my_timer, my_timer_callback, 0);

    // 3. 激活定时器(设置为 3 秒后触发)
    my_timer.expires = jiffies + msecs_to_jiffies(3000);
    add_timer(&my_timer);

    return 0;
}

static void __exit my_module_exit(void)
{
    // 5. 安全地删除定时器
    del_timer_sync(&my_timer);
    printk(KERN_INFO "模块卸载...\n");
}

module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");