当前位置: 首页 > news >正文

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


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_timehrtimer_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.oKDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)all:$(MAKE) -C $(KDIR) M=$(PWD) modulesclean:$(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 内核中使用这两种不同精度的定时器,帮助你根据实际需求进行选择。

http://www.dtcms.com/a/283385.html

相关文章:

  • 切比雪夫距离
  • Python高级编程技巧探讨:装饰器、Patch与语法糖详解
  • 二叉树思想草稿
  • 关于SaaS业务模式及其系统架构构建的详细解析
  • RICE-YOLO:基于改进YOLOv5的无人机稻穗检测新方法
  • 【C语言网络编程】HTTP 客户端请求(发送请求报文过程)
  • 在UE中如何操作视图的大小,方位,移动
  • 16路串口光纤通信FPGA项目实现指南 - 第二部分(上)
  • 【LeetCode刷题指南特别篇】--移除链表元素,调试技巧,链表分割
  • pyJianYingDraft 在 import_srt 字幕添加花字效果
  • 鸿蒙系统账号与签名内容整理
  • CG--资料分析1
  • 重载操作类型
  • 向量数据库Faiss vs Qdrant全面对比
  • ESP32——快速入门
  • 【深度学习】学习算法中为什么要使用“小批量随机”
  • 前后端分离项目中的接口设计与调用流程——以高仙机器人集成为例
  • CSS动画与变换全解析:从原理到性能优化的深度指南
  • JAVA同城自助羽毛球馆线上预约系统源码支持微信公众号+微信小程序+H5+APP
  • HugeGraph 【图数据库】JAVA调用SDK
  • 助力品牌从系统碎片化走向IT一体化建设,实现全渠道业务协同!——商派“数智化IT轻咨询”
  • MH32F103A单片机 可兼容替代STMCCT6/RCT6/RBT6,增强型
  • Kotlin重写函数中的命名参数
  • 【论文阅读】A Survey on Knowledge-Oriented Retrieval-Augmented Generation(4)
  • 【Android】CheckBox实现和监听
  • 归一化与激活函数:深度学习的双引擎
  • CentOS网络配置与LAMP环境搭建指南
  • Product Hunt 每日热榜 | 2025-07-16
  • 计算机网络——数据链路层(25王道最新版)
  • Oracle 关于一些连接故障的总结