了解內核定時相關基礎知識
文章目錄
- 簡要介紹
- timer_list 特點
- API 函數
- 實驗
- 測試程序 - timer_mod.c
- 編譯文件-Makefile
- 實驗驗證
- 注意事項
- 總結
簡要介紹
硬件為內核提供了一個系統定時器來計算流逝的時間(即基于未來時間點的計時方式, 以當前時刻為計時開始的起點, 以未來的某一時刻為計時的終點) , 內核只有在系統定時器的幫助下才能計算和管理時間, 但是內核定時器的精度并不高, 所以不能作為高精度定時器使用。并且內核定時器的運行沒有周期性, 到達計時終點后會自動關閉。 如果要實現周期性定時, 就要在定時處理函數中重新開啟定時器。
Linux 內核中使用 timer_list 結構體表示內核定時器, 該結構體定義在“內核源碼/include/li
nux/timer.h”文件中, 具體內容如下所示
struct timer_list {struct hlist_node entry;unsigned long expires;/* 定時器超時時間,單位是節拍數 */void (*function)(struct timer_list *);/* 定時處理函數 */u32 flags;#ifdef CONFIG_LOCKDEPstruct lockdep_map lockdep_map;
#endifANDROID_KABI_RESERVE(1);ANDROID_KABI_RESERVE(2);
};
timer_list 特點
inux 內核定時器是基于 timer_list 結構的動態定時器,具有以下特點:
-
不是周期性的,超時后會自動失效
-
基于內核的 jiffies 計時
-
在中斷上下文執行,因此不能睡眠
-
精度取決于 HZ 值(通常為 1ms 或 10ms)
API 函數
函數 | 作用 |
---|---|
void add_timer(struct timer_list *timer) | 向 Linux 內核注冊定時器,使用 add_timer 函數向內核注冊定時器以后,定時器就會開始運行 |
int del_timer(struct timer_list * timer) | 刪除一個定時器 |
int mod_timer(struct timer_list *timer,unsigned long expires) | 修改定時值,如果定時器還沒有激活的話,mod_timer 函數會激活定時器 |
在使用add_timer()函數向 Linux 內核注冊定時器之前,還需要設置定時時間,定時時間由timer_list結構體中的expires參數所確定,單位為節拍數
這里簡要理解,節拍數 jiffies 和 時間之間的轉換函數:
jiffies_64用于64位系統,而jiffies用于 32 位系統。為了方便開發,Linux 內核還提供了幾個jiffies和ms、us、ns之間的轉換函數,如下 所示:
函數 | 作用 |
---|---|
int jiffies_to_msecs(const unsigned long j) | 將 jiffies 類型的參數 j 分別轉換為對應的毫秒 |
int jiffies_to_usecs(const unsigned long j) | 將 jiffies 類型的參數 j 分別轉換為對應的微秒 |
u64 jiffies_to_nsecs(const unsigned long j) | 將 jiffies 類型的參數 j 分別轉換為對應的納秒 |
long msecs_to_jiffies(const unsigned int m) | 將毫秒轉換為 jiffies 類型 |
long usecs_to_jiffies(const unsigned int u) | 將微秒轉換為 jiffies 類型 |
unsigned long nsecs_to_jiffies(u64 n) | 將納秒轉換為 jiffies 類型 |
既然要定時,那么情形就是 把定時時間轉換成節拍數,系統內核根據節拍數和節拍頻率。內核里面只認節拍數,它對應的就是頻率
例如:進行3秒鐘的定時:
timer_test.expires = jiffies_64 +msecs_to_jiffies(3000)
實驗
測試程序 - timer_mod.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>static void function_test(struct timer_list *t);//定義function_test定時功能的函數
DEFINE_TIMER(timer_test,function_test);static void function_test(struct timer_list *t){printk(" this is function test \n");mod_timer(&timer_test,jiffies_64+msecs_to_jiffies(5000)); 使用mod_timer函數將定時時間設置為五秒后}static int __init time_module_init(void) //驅動入口函數
{timer_test.expires = jiffies_64 + msecs_to_jiffies(5000);//將定時時間設置為五秒后add_timer(&timer_test);//添加一個定時器return 0;}
static void __exit time_module_exit(void) //驅動出口函數
{del_timer(&timer_test);//刪除一個定時器printk("module exit \n");}
module_init(time_module_init);
module_exit(time_module_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("wang fang chen");
源碼分析
-
這里用到了定時器timer_list 的三個api 函數:
定義定時器:DEFINE_TIMER(timer_test,function_test); 參數為定時器名稱和回調方法
添加定時器:add_timer(&timer_test);
修改定時器:mod_timer(&timer_test,jiffies_64+msecs_to_jiffies(5000));
刪除定時器:del_timer(&timer_test);**注意點:**定時器的名稱并不是作為變量定義的,作為方法參數定義了的 DEFINE_TIMER的方法參數。
回調函數 function_test 是先定義,然后實現 。
編譯文件-Makefile
#!/bin/bash
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
obj-m += timer_mod.o
KDIR :=/home/wfc123/Linux/rk356x_linux/kernel
PWD ?= $(shell pwd)
all:make -C $(KDIR) M=$(PWD) modulesclean:make -C $(KDIR) M=$(PWD) clean
實驗驗證
加載驅動看打印信息
注意事項
-
定時器精度:內核定時器的精度受 HZ 值影響,不適合需要高精度的場合
-
執行上下文:回調函數在中斷上下文執行,不能調用可能睡眠的函數
-
多核處理:del_timer() 可能在 SMP 系統上返回后定時器仍在運行,使用 del_timer_sync() 更安全
-
競爭條件:確保在模塊退出時刪除所有定時器
總結
這里了解的是內核的一個定時器timer_list 的使用。 了解基本知識即可。