内核定时器
概念
Linux内核定时器使用基于时间点的计时方式:以当前时刻为启动时间点,以未来的某一时刻为终止点。
内核定时器精度不高
内核定时器不是周期性运行的,超时后会自动关闭。可以在定时器处理函数中重新开启定时器。
结构体和函数
include/linux/timer.h
timer_list结构体
struct timer_list{
struct list_head entry;
unsigned long expires; //定时器的超时节拍数
struct tvec_base *base;
void (*function)(unsigned long); //定时处理函数
unsigned long data; //传递给定时处理函数的参数
int slack;
};
expires需要的是节拍数,并不是时间参数
变量
内核中全局变量jiffies表示当前的节拍数,宏HZ表示一秒对应的时钟节拍数。
内核有一个宏HZ,表示一秒对应的节拍数。CONFIG_HZ可以在编译内核的时候配置Kernel Features ---- Timer frequency选项里。视频教程里一秒默认节拍是100hz,即一秒的节拍数是100.
时间转换函数
ms转换为时钟节拍
unsigned long msecs_to_jiffies(const unsigned int m)
us转换为时钟节拍
unsigned long usecs_to_jiffies(const unsigned int u)
定时10ms:expires = jiffies + msecs_to_jiffies(10)
定时10us:expires = jiffies + usecs_to_jiffies(10)
初始化宏
静态定义结构体变量并初始化function,expires,data成员
#define DEFINE_TIMER(_name, _function, _expires, _data)
//参数
_name变量名,用于初始化的timer_list结构体
_function超时处理函数,
_expires到点时间,节拍
_data传递给超时处理函数的参数
add_timer函数
void add_timer(struct timer_list *timer)
向Linux内核注册定时器,使用add_timer注册定时器后,定时器就会开始运行
del_timer函数
int del_timer(struct timer_list *timer)
删除一个定时器,不管定时器是否激活,都可以使用此函数删除。
在多处理器系统上,定时器可能会在其他的处理器上运行,因此调用删除定时器时会等待其他处理器的定时处理函数退出。
返回值:0,定时器还未被激活;1,定时器已经激活
mod_timer函数
修改定时值,如果定时器还没有激活,调用函数会激活定时器
int mod_timer(struct timer_list *timer, unsigned long expires)
返回值:0,定时器还未被激活;1,定时器已经激活
在定时器处理函数里可以调用此函数重新设置定时器。
实验思路
- 使用DEFINE_TIMER宏定义并初始化定时器
- 定义编写定时器处理函数,在函数里可以通过mod_timer函数修改并再次激活定时器
- 在模块入口处使用add_timer函数向内核注册定时器,并在此重新编辑定时器结构体的定时节拍函数
- 模块出口处,del_timer释放定时器
代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/timer.h>
//声明定时器处理函数
static void ta_timer_func(unsigned long dat);
//定义、初始化定时器,expires参数不必再此初始化,在需要开启的时候再计算节拍
DEFINE_TIMER(taxue_timer, ta_timer_func, 0, 0);
//定义定时器处理函数
static void ta_timer_func(unsigned long dat){
static int count=0;
printk("hello timer count=%d\n",count++);
if(count <= 50){
//修改定时器,让定时器再次工作
//节拍设置为当前时间+count*10毫秒
mod_timer(&taxue_timer, jiffies + msecs_to_jiffies(count*10));
}else{
printk("done\n");
}
}
static int driver_init_timer(void){
printk("Hello TaXue\n");
taxue_timer.expires = jiffies + 1*HZ; //当前节拍+1秒节拍
add_timer(&taxue_timer); //向内核注册定时器
return 0;
}
static void driver_exit_timer(void){
del_timer(&taxue_timer); //删除定时器
printk("platform driver exit!\n");
}
module_init(driver_init_timer); //模块入口,加载驱动时执行参数内的函数
module_exit(driver_exit_timer); //模块出口,卸载模块时执行参数内的函数
MODULE_LICENSE("Dual BSD/GPL"); //遵循BSD和GPL开源许可
MODULE_AUTHOR("TAXUE"); //模块作者
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术