低精度定时器 (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.pswp.cn/diannao/91386.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/91386.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

虚拟机VMware的使用方法

虚拟机VMware的使用方法VMware是全球领先的虚拟化技术提供商&#xff0c;其产品&#xff08;如VMware Workstation Pro&#xff09;允许用户在单一物理机上运行多个操作系统&#xff08;OS&#xff09;&#xff0c;实现资源高效利用、隔离测试和灵活部署。本文将详细介绍VMware…

冰岛人(map)

#include<bits/stdc.h> using namespace std; struct people { string fat; int sex; }; map<string,people>mp; int pan(string s,string m) { string s1; int i0; while(s!“”) { int y0; s1m; while(s1!“”) { if(s1s&&(i<4||y<4)) return 0; s…

MS Azure Eventhub 发送 AD log 到cribl

1: 首先说一下,Cribl 提供了很多第三方的接口: 先看一下cribl 提供的接口界面: 注意到,上面提供的link 地址是 xxxxx:9093, 不鼠标放到撒谎给你吗的? 上面,就可以看到了。所以要开的port 一定要把9093 开了,关于全部开的port: What ports do I need to open on the f…

电力名词通俗解析5:计量系统

## 电网计量系统通俗讲解&#xff1a;南网视角下的电力“精算师”想象一下&#xff0c;城市电网如同一个庞大而精密的“能量河流”&#xff0c;千家万户、工厂企业都在从中取水&#xff08;用电&#xff09;。如何精确计量每家用了多少“水”&#xff1f;如何确保“河流”输送中…

关于redis各种类型在不同场景下的使用

Redis 提供了多种数据结构类型,每种类型适用于不同的场景。以下是 Redis 主要数据类型及其典型应用场景的详细说明: 1. String(字符串) 特点:最简单的键值存储,值可以是字符串、整数或二进制数据(最大 512MB)。 适用场景: 缓存:存储用户会话、网页内容等(如 SET u…

Vue 3 动态ref问题

目录 1.问题描述 2.示例代码 3.原因分析 4.解决方案 5.总结 1.问题描述 在Vue 3项目中&#xff0c;当使用动态ref来引用组件时&#xff0c;删除组件后发现ref对象中对应的key仍然存在&#xff0c;只是值变为null&#xff0c;而不是完全删除该key。 在一个可拖拽的卡片列表…

lazyvim恢复gt键

好的&#xff01;下面是一个完整的 LazyVim 键位配置 patch&#xff0c;将 gt / gT 恢复为 “切换标签页&#xff08;tab page&#xff09;” 的原始行为&#xff0c;同时保留原本 buffer 切换功能在其他键位上&#xff08;比如 / &#xff09;。 ⸻ ✅ 恢复 gt 为 Tab 切换&a…

React Native 在 Web 前端跨平台开发中的优势与实践

React Native 在 Web 前端跨平台开发中的优势与实践 对于广大 Web 前端开发者而言&#xff0c;移动端开发似乎总隔着一层“原生”的壁垒。学习 Swift/Kotlin、熟悉 Xcode/Android Studio 的高昂成本&#xff0c;让许多人望而却步。然而&#xff0c;“一次编写&#xff0c;多端运…

QT控件 使用QtServer系统服务实现搭建Aria2下载后台服务,并使用Http请求访问Json-RPC接口调用下载退出

前言 最近了解到qt-solutions这个开源项目,仔细研究一番&#xff0c;发现其中的QtServer项目能在Windows系统中创建系统服务&#xff0c;Linux/Unix系统中能作为守护进程使用&#xff0c;之前一直以为编写服务需要使用Windows api来实现&#xff0c;没想到这么简单。 本来之前就…

Python中关于数组的常见操作

Python中关于数组的常见操作 1.创建数组 array []2.添加元素 array.append()3.访问元素 print(array[2])通过索引进行数组元素的访问 4.修改元素 array[2] 3直接对想修改的元素位置进行赋值 5.删除元素 array.remove(2) #删除元素2del array[2] #删除索引为2的元素6…

Image 和 IMU 时间戳同步

1 目录 时间戳同步介绍 时间戳同步初探 时间戳获取方式 时间戳延迟估计方法 姿态补偿 匀速模型在 Bundle Adjustment 中的应用 重投影残差 视觉特征匀速运动补偿特征坐标 重投影残差 基于特征匀速模型算法的实验结果 轨迹匀速模型 vs 特征匀速模型 时间戳同步算法扩…

创建linux端口映射连接小网

&#x1f680; 方法 1&#xff1a;在执行机上配置 SSH 服务端转发 这个做法是在 执行机上配置一个常驻 SSH 隧道&#xff0c;把大网的某个端口长期转发到小网单板的 22 端口。 &#x1f468;‍&#x1f4bb; 操作步骤 1️⃣ 在执行机上创建一个 systemd 服务 假设&#xff1a; …

了解Java21

目前还没有实操过从java8/java11直接到java17,java21。 先储备下知识点&#xff0c;写一些简单例子&#xff0c;以便后续的实操。 一些新特性&#xff08;java8之后的&#xff09; var变量 和前端js定义变量一样了&#xff0c;var搞定public static void main(String[] args) {…

【代码】基于CUDA优化的RANSAC实时激光雷达点云地面分割

基于CUDA优化的RANSAC实时激光雷达点云地面分割 摘要&#xff1a; 本文介绍了一个高性能的激光雷达&#xff08;LiDAR&#xff09;地面分割项目。该项目基于RANSAC平面估计算法&#xff0c;并通过深度CUDA并行优化&#xff0c;将核心处理时间从近100ms缩短至10ms以内&#xff…

vuex原理以及实现

vuex官方文档 Vuex是什么&#xff1f; Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态&#xff0c;并以相应的规则保证状态以一种可预测的方式发生变化 每一个 Vuex 应用的核心就是 store&#xff08;仓库&#xff09;。“stor…

APIs案例及知识点串讲(上)

一.轮播图专题CSS代码<style>* {box-sizing: border-box;}.slider {width: 560px;height: 400px;overflow: hidden;}.slider-wrapper {width: 100%;height: 320px;}.slider-wrapper img {width: 100%;height: 100%;display: block;}.slider-footer {height: 80px;backgro…

华大单片机HC32L110烧录程序方法

1&#xff0c;安装J-flash工具 从SEGGER官网下载J-flash工具&#xff0c;地址&#xff1a;SEGGER - The Embedded Experts - Downloads - J-Link / J-Trace。按向导安装完成。 2&#xff0c;使用如下图JLINK工具SWD模式连接单片机的烧录接口&#xff08;SWDIO,SWCLK,GND&#…

LeetCode|Day15|125. 验证回文串|Python刷题笔记

LeetCode&#xff5c;Day15&#xff5c;125. 验证回文串&#xff5c;Python刷题笔记 &#x1f5d3;️ 本文属于【LeetCode 简单题百日计划】系列 &#x1f449; 点击查看系列总目录 >> &#x1f4cc; 题目简介 题号&#xff1a;125. 验证回文串 难度&#xff1a;简单 题…

项目学习笔记 display从none切换成block

跟着视频学做项目的时候&#xff0c;碰到一个多级联动列表&#xff0c;列表元素的display会从none切换成block&#xff0c;切换过程中可能导致资源渲染过多&#xff0c;从而导致卡顿问题。<div class"all-sort-list2"><div class"item" v-for&quo…

从 “洗澡难” 到 “洗得爽”:便携智能洗浴机如何重塑生活?

洗澡本应是日常生活的简单需求&#xff0c;但对于失能老人、行动不便者而言&#xff0c;却可能成为一项充满挑战甚至危险的“艰巨任务”。中国失能、半失能老年人口超过4200万&#xff0c;传统助浴方式存在搬运风险高、隐私难以保障、效率低下等问题&#xff0c;使得“洗澡难”…