GoDm@'s Blog

linux内核定时器的使用

版权信息

warning

本文章为博主原创文章。遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。


Linux 内核定时器用于在未来的某个特定时间点(基于系统滴答 jiffies)执行某个函数。它们是低精度定时器,主要用于超时处理、轮询检测等不需要纳秒级精度的场景。它是内核中最基础、最轻量级的异步机制之一。

1. 核心概念

在使用之前,需要理解以下几个关键点:

2. 核心数据结构

在 Linux 4.14 版本之后,内核定时器的 API 进行了重构,使得结构更加简单安全。

struct timer_list {
    /* 核心字段 */
    unsigned long expires;        // 超时时间(单位:jiffies)
    void (*function)(struct timer_list *); // 超时后的回调函数
    u32 flags;                    // 标志位
    /* 内部字段,用户通常无需直接操作 */
    struct hlist_node entry;
    unsigned long data;           // 旧版本残留,新版通常通过container_of 获取数据
};

3. 内核定时器的特点

如果需要高精度定时,应该使用 hrtimer(高精度定时器)。

4. 基本使用流程

  1. 定义一个 struct timer_list 变量
  2. 初始化定时器
  3. 设置回调函数
  4. 设置超时时间
  5. 注册定时器(添加到内核)
  6. 在退出时删除定时器

5. 常用API函数

以下是开发中最常用的 API,请务必掌握:

5.1. timer_setup():初始化

内核 4.15 之后推荐的初始化方法。

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

参数说明:

5.2. mod_timer():修改

修改定时器的超时时间(如果未启动则相当于启动)。

int mod_timer(struct timer_list *timer, unsigned long expires);

参数说明:

例:过 200ms 触发:

mod_timer(&my_timer, jiffies + msecs_to_jiffies(200));

5.3. del_timer():删除

删除一个定时器(不等待当前回调执行完)。

int del_timer(struct timer_list *timer);

5.4. del_timer_sync():删除

删除并确保回调不再执行(常用于模块卸载)。

int del_timer_sync(struct timer_list *timer);

推荐在驱动卸载 (module_exit) 中使用。

5.5. 时间转换函数(非常重要)

Linux 内核定时器以 jiffies 为单位,但我们通常希望以毫秒、秒表示时间。
直接操作 jiffies 很麻烦,内核提供了转换宏:

unsigned long msecs_to_jiffies(const unsigned int m);
unsigned long jiffies_to_msecs(const unsigned long j);
unsigned long usecs_to_jiffies(const unsigned int u);

示例

unsigned long expires = jiffies + msecs_to_jiffies(500);

6. 使用示例:每 500ms 打印一次消息

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

static struct timer_list my_timer;

static void my_timer_callback(struct timer_list *t)
{
    pr_info("my_timer: timer fired!\n");

    /* 再次启动定时器,形成周期性 */
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(500));
}

static int __init timer_example_init(void)
{
    pr_info("my_timer: module init\n");

    /* 初始化定时器并绑定回调函数 */
    timer_setup(&my_timer, my_timer_callback, 0);

    /* 启动一个 500ms 的定时器 */
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(500));

    return 0;
}

static void __exit timer_example_exit(void)
{
    pr_info("my_timer: module exit\n");

    /* 删除定时器并确保回调不再运行 */
    del_timer_sync(&my_timer);
}

module_init(timer_example_init);
module_exit(timer_example_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("GoDm@");
MODULE_DESCRIPTION("Kernel timer example");

7. 常见问题FAQ

7.1. ❓定时器回调能否睡眠?

不能。

定时器回调运行在 软中断(softirq)上下文,不允许调用会睡眠的 API。
例如不能使用:

7.2. ❓定时器精度是多少?

取决于 CONFIG_HZ

HZ 定时器最小精度
100 10ms
250 4ms
1000 1ms

如果你要亚毫秒或更高精度 → 使用 hrtimer

7.3. ❓卸载模块时忘记删除定时器会怎样?

很可能导致:

7.4. ❓内核定时器的时钟源是什么?

8. 更高级:定时器 + 私有数据

如果你需要为每个定时器绑定自己的数据,可以这样:

struct my_data {
    int count;
    struct timer_list timer;
};

static void my_callback(struct timer_list *t)
{
    struct my_data *data = from_timer(data, t, timer);
    pr_info("count = %d\n", data->count++);
}

from_timer() 在新内核中非常常用。


共计约1.2k字。于2025/12/08首次发布,最后更新于2025/12/08。

本文章为博主原创文章。遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

AI辅助创作:本文部分内容由 ChatGPT 5 生成,最终版本由作者审核与修改。了解该AI模型

  1. 1. 核心概念
  2. 2. 核心数据结构
  3. 3. 内核定时器的特点
  4. 4. 基本使用流程
  5. 5. 常用API函数
    1. 5.1. timer_setup():初始化
    2. 5.2. mod_timer():修改
    3. 5.3. del_timer():删除
    4. 5.4. del_timer_sync():删除
    5. 5.5. 时间转换函数(非常重要)
  6. 6. 使用示例:每 500ms 打印一次消息
  7. 7. 常见问题FAQ
    1. 7.1. ❓定时器回调能否睡眠?
    2. 7.2. ❓定时器精度是多少?
    3. 7.3. ❓卸载模块时忘记删除定时器会怎样?
    4. 7.4. ❓内核定时器的时钟源是什么?
  8. 8. 更高级:定时器 + 私有数据