低精度定時器 (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
英文地址,請注明出處:http://en.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;使得“洗澡難”…