低精度定时器 (timer_list) 和 高精度定时器 (hrtimer)

发布于:2025-07-17 ⋅ 阅读:(25) ⋅ 点赞:(0)

Linux 内核提供了两种主要类型的定时器,以满足不同的时间精度需求:低精度定时器 (timer_list)高精度定时器 (hrtimer)。它们各有特点和适用场景。下面,我将分别提供它们在内核代码中的简化使用示例。


1. 低精度定时器 (timer_list) 示例

timer_list 是最基本的定时器,精度依赖于系统时钟节拍(jiffies),通常是毫秒级。它适用于对时间精度要求不高,允许一定误差的周期性或延迟任务。

特点:

  • 精度:毫秒级(受 HZ 限制)。

  • 开销:较小。

  • 用途:简单的后台任务、超时检测、不频繁的周期性事件。

使用步骤:

  1. 定义 struct timer_list 变量。

  2. 初始化 timer_list 使用 timer_setup()init_timer()(较旧)指定回调函数。

  3. 设置定时器: 使用 mod_timer() 设定到期时间。

  4. 删除定时器: 使用 del_timer()del_timer_sync()(等待回调完成)在不再需要时清理。

代码示例:

这是一个简单的内核模块,它会设置一个定时器,在 5 秒后首次触发,然后每隔 3 秒周期性地打印一条消息。

C

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/jiffies.h> // 用于 jiffies 变量

// 1. 定义一个 timer_list 结构体变量
static struct timer_list my_low_res_timer;
static int counter = 0;

// 2. 定义定时器到期时要执行的回调函数
void low_res_timer_callback(struct timer_list *t)
{
    // 获取 timer_list 结构体变量的指针
    // (如果 timer_setup() 的 data 参数不为空,也可以通过 t->data 获取)
    // 或者直接使用全局变量 my_low_res_timer,这里不需要特殊的解引用

    pr_info("Low-res timer triggered! Counter: %d\n", ++counter);

    // 如果需要周期性触发,必须重新设置定时器
    // 这里设置为从现在起,每隔 3 秒触发一次
    mod_timer(&my_low_res_timer, jiffies + HZ * 3);
}

// 模块初始化函数
static int __init my_low_res_init(void)
{
    pr_info("Loading low-res timer module...\n");

    // 3. 初始化定时器:绑定回调函数
    // timer_setup(&my_low_res_timer, low_res_timer_callback, 0);
    // 从 Linux 4.14+,推荐使用 timer_setup。第三个参数是 data,这里不需要就设为0。
    timer_setup(&my_low_res_timer, low_res_timer_callback, 0);


    // 4. 设置并启动定时器:5 秒后首次触发
    // jiffies 是当前系统启动以来的时钟节拍数
    // HZ 是每秒的节拍数(通常是 100、250 或 1000)
    mod_timer(&my_low_res_timer, jiffies + HZ * 5);

    pr_info("Low-res timer armed for 5 seconds (initial) and then every 3 seconds.\n");
    return 0;
}

// 模块退出函数
static void __exit my_low_res_exit(void)
{
    // 5. 删除定时器:防止模块卸载后定时器仍然触发
    // del_timer_sync() 会等待回调函数执行完毕,更安全
    del_timer_sync(&my_low_res_timer);
    pr_info("Low-res timer module unloaded and timer deleted.\n");
}

module_init(my_low_res_init);
module_exit(my_low_res_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple low-resolution timer example module.");

dump_stack输出,其使用softirq: 

[200643.659695] Call trace:
[200643.662215]  dump_backtrace+0x9c/0x100
[200643.666045]  show_stack+0x20/0x40
[200643.669435]  dump_stack_lvl+0x90/0xb0
[200643.673174]  dump_stack+0x18/0x28
[200643.676563]  low_res_timer_callback+0x3c/0x60 [low_res_timer_example]
[200643.683081]  call_timer_fn+0x3c/0x158
[200643.686819]  __run_timers+0x340/0x3a8
[200643.690555]  run_timer_softirq+0x28/0x50
[200643.694551]  handle_softirqs+0x178/0x3b8
[200643.698549]  __do_softirq+0x1c/0x28
[200643.702112]  ____do_softirq+0x18/0x30
[200643.705849]  call_on_irq_stack+0x24/0x58
[200643.709846]  do_softirq_own_stack+0x24/0x38
[200643.714102]  irq_exit_rcu+0xd0/0x110
[200643.717752]  el1_interrupt+0x38/0x58
[200643.721403]  el1h_64_irq_handler+0x18/0x28
[200643.725575]  el1h_64_irq+0x64/0x68
[200643.729050]  default_idle_call+0x80/0x148
[200643.733136]  do_idle+0x298/0x310
[200643.736438]  cpu_startup_entry+0x3c/0x50
[200643.740435]  rest_init+0xd0/0xd8
[200643.743738]  arch_call_rest_init+0x18/0x20
[200643.747913]  start_kernel+0x53c/0x6b8
[200643.751650]  __primary_switched+0xbc/0xd0

 


2. 高精度定时器 (hrtimer) 示例

hrtimer 提供纳秒级的精确控制,不依赖于 jiffies,并且能主动唤醒 CPU。它适用于实时系统、多媒体处理、电源管理等对时间精度有严格要求的场景。

特点:

  • 精度:纳秒级(使用硬件定时器)。

  • 开销:相对较大。

  • 用途:实时控制、高频事件、精确的延迟和唤醒。

使用步骤:

  1. 定义 struct hrtimer 变量。

  2. 初始化 hrtimer 使用 hrtimer_init() 指定时钟源和定时器模式。

  3. 设置回调函数: 将回调函数指针赋值给 hrtimer->function

  4. 设置到期时间: 使用 ktime_set() 创建 ktime_t 类型的时间值。

  5. 启动定时器: 使用 hrtimer_start()

  6. 取消定时器: 使用 hrtimer_cancel()hrtimer_try_to_cancel()

代码示例:

这是一个内核模块,它会设置一个高精度定时器,每隔 500 微秒(0.5 毫秒)触发一次。

C

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h> // 高精度定时器头文件
#include <linux/ktime.h>   // ktime_t 时间类型

// 1. 定义一个 hrtimer 结构体变量
static struct hrtimer my_high_res_timer;
static ktime_t interval_time; // 定义定时器间隔时间
static int hr_counter = 0;

// 2. 定义定时器到期时要执行的回调函数
// 回调函数必须返回 enum hrtimer_restart 类型
enum hrtimer_restart high_res_timer_callback(struct hrtimer *timer)
{
    pr_info("High-res timer triggered! HR_Counter: %d\n", ++hr_counter);

    // 如果需要周期性触发,必须重新设置到期时间
    // hrtimer_forward_now() 会在当前时间的基础上,向前推进到下一个间隔点
    hrtimer_forward_now(timer, interval_time);

    return HRTIMER_RESTART; // 表示定时器需要重新启动
}

// 模块初始化函数
static int __init my_high_res_init(void)
{
    pr_info("Loading high-res timer module...\n");

    // 3. 初始化 hrtimer
    // CLOCK_MONOTONIC: 单调递增时钟(不受系统时间调整影响)
    // HRTIMER_MODE_REL: 相对模式(时间间隔相对于当前时间)
    hrtimer_init(&my_high_res_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);

    // 4. 设置回调函数
    my_high_res_timer.function = high_res_timer_callback;

    // 5. 设置定时器间隔:500 微秒 (0.5毫秒)
    // ktime_set(seconds, nanoseconds)
    interval_time = ktime_set(0, 500 * 1000); // 0秒,500000纳秒 = 500微秒  //实际测试需要将时间改大一点避免打印过多。

    // 6. 启动定时器:首次到期时间设置为 interval_time
    hrtimer_start(&my_high_res_timer, interval_time, HRTIMER_MODE_REL);

    pr_info("High-res timer armed for every 500 microseconds.\n");
    return 0;
}

// 模块退出函数
static void __exit my_high_res_exit(void)
{
    // 7. 取消定时器:防止模块卸载后定时器仍然触发
    // hrtimer_cancel() 返回 0 表示定时器未激活,非 0 表示已取消
    int ret = hrtimer_cancel(&my_high_res_timer);
    if (ret) {
        pr_info("High-res timer was still active and cancelled.\n");
    } else {
        pr_info("High-res timer was not active.\n");
    }
    pr_info("High-res timer module unloaded.\n");
}

module_init(my_high_res_init);
module_exit(my_high_res_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple high-resolution timer example module.");

dump_stack输出:  

[  220.944207][    C2] Call trace:
[  220.947334][    C2]  dump_backtrace+0xa0/0x128
[  220.951765][    C2]  show_stack+0x20/0x38
[  220.955761][    C2]  dump_stack_lvl+0x78/0xc8
[  220.960106][    C2]  dump_stack+0x18/0x28
[  220.964102][    C2]  high_res_timer_callback+0x40/0x78 [high_res_timer_example]
[  220.971399][    C2]  __run_hrtimer+0x84/0x270
[  220.975743][    C2]  __hrtimer_run_queues+0xb4/0x140
[  220.980694][    C2]  hrtimer_interrupt+0x10c/0x348
[  220.985471][    C2]  arch_timer_handler_phys+0x34/0x58
[  220.990596][    C2]  handle_percpu_devid_irq+0x90/0x1c8
[  220.995807][    C2]  handle_irq_desc+0x48/0x68
[  221.000238][    C2]  generic_handle_domain_irq+0x24/0x38
[  221.005537][    C2]  __gic_handle_irq_from_irqson+0x18c/0x2c8
[  221.011268][    C2]  gic_handle_irq+0x2c/0xb0
[  221.015612][    C2]  call_on_irq_stack+0x24/0x30
[  221.020217][    C2]  do_interrupt_handler+0x88/0x98
[  221.025082][    C2]  el1_interrupt+0x54/0x120
[  221.029426][    C2]  el1h_64_irq_handler+0x24/0x30
[  221.034203][    C2]  el1h_64_irq+0x78/0x80
[  221.038285][    C2]  default_idle_call+0x74/0x150
[  221.042977][    C2]  cpuidle_idle_call+0x18c/0x200
[  221.047754][    C2]  do_idle+0xbc/0x188
[  221.051578][    C2]  cpu_startup_entry+0x40/0x50
[  221.056182][    C2]  secondary_start_kernel+0x14c/0x1d8
[  221.061395][    C2]  __secondary_switched+0xb8/0xc0

 


编译和加载

要编译这些模块,你需要一个 Makefile

Makefile

obj-m := low_res_timer_example.o high_res_timer_example.o

KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean

然后,在 Linux 系统上执行:

  1. make

  2. sudo insmod low_res_timer_example.kosudo insmod high_res_timer_example.ko

  3. 通过 dmesg -w 观察内核日志输出。

  4. sudo rmmod low_res_timer_examplesudo rmmod high_res_timer_example

这些例子展示了如何在 Linux 内核中使用这两种不同精度的定时器,帮助你根据实际需求进行选择。


网站公告

今日签到

点亮在社区的每一天
去签到