前言
这段时间我会把内核的小模块复习起来并记录成文章给大家发表出来,方便大家来学习,大家感兴趣的话可以点赞关注下,顺便说下想第一时间看我的文章的话可以点击公众号主页右上角有个设为星标,以免错过好文。先从定时器开始学习。
内核定时器简介
定时器是一个很常用的功能,需要周期性处理的工作都要用到定时器, 基本做驱动开发的人都要接触用到的。内核定时器因为采用系统时钟属于软件定时器所以他精度不高,不能作为高精度定时器使用,用法和硬件定时器一样,只不过不需要初始化一大堆寄存器而工作,需要注意一点是默认执行一次就结束了并不是周期性运行的,因此如果要想实现周期性的定时,就需要在定时处理函数中重新开启定时器。
内核定时器的数据结构
Linux内核使用 timer_list 结构体表示内核定时器, timer_list 定义在文件include/linux/timer.h 中
struct timer_list {
/*
¦* All fields that change during normal runtime grouped to the
¦* same cacheline
¦*/
struct hlist_node entry;
unsigned long expires; //定时器的超时时间,单位不是时长,单位是节拍数
void (*function)(struct timer_list *);//定时处理函数
u32 flags;
unsigned long cust_data;//传递给function函数的参数
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
...
};
timer_list 中有三个重要参数:
-
unsigned long expires; /* 期望定时器要执行的 jiffies 值也就是超时时间,单位是节拍数jiffies */ -
void (*function)(struct timer_list *); / 到达该 jiffies 值时,会调用function定时处理函数/ -
unsigned long data; / 要传递给 function 函数的参数 */
其中 expires 字段表示期望定时器执行的 jiffies 节拍值,到达该 jiffies 值时,将调用 function 函数,并传递 data 作为参数。当一个定时器被注册到内核之后,entry 字段用来连接该定时器到一个内核链表中。
HZ表示一秒的节拍数, 也就是频率。系统的节拍率一般看系统内核宏.config文件里CONFIG_HZ,比如CONFIG_HZ=100意思是1s钟经过100HZ, 100HZ的节拍率的时间精度是10ms。
例如定义一个2s的周期定时器,那么这个定时器期望的超时时间是expires = jffies + 2*HZ / expires = jffies + msecs_to_jiffies(2000)
注意:expires是32位的,因此内核定时器并不适用于长的未来时间点,还有有计数就会溢出的风险,溢出以后会重新从0开始
内核定时器的API函数
del_timer_sync和del_timer的区别: del_timer_sync 不能用在中断上下文中
Linux 内核提供了几个 jiffies 和 ms、us、ns 之间的转换函数
内核定时器的处理流程
1、定义一个定时器
2、编写定时器回调函数(如果需要定时器周期性运行的话就使用 mod_timer)
3、定时器初始化
1、) 初始化定时器
方法一:
DEFINE_TIMER(timer_name, function_name, expires_value, data);
该宏会定义一个名叫 timer_name 内核定时器,并初始化其 name, function, expires 和 base 字段。
方法二:
struct timer_list mytimer; void init_timer(struct timer_list *timer);
上述init_timer函数将初始化struct timer_list的 entry的next 为 NULL ,并为base指针赋值
2、) 设置定时处理函数
void (*function)(struct timer_list *)
3、) 设置超时时间
4、) 设置处理函数的参数(可有可无)
5、) 启动定时器
add_timer用于向Linux内核中注册一个新的定时器,该定时器一旦被注册,定时器就会开始运行。
void add_timer (struct timer_list *timer);
4、删除定时器
del_timer用于删除指定的定时器,不管该定时器是否被激活,都可以被删除。
int del_timer (struct timer_list *timer);
或
int del_timer_sync(struct timer_list *timer)
内核定时器demo
例如定时器500ms的demo如下:
struct timer_list timer;
/* 定时器回调函数 */
void timer_function(unsigned long arg)
{
printk("timer function callback\n");
mod_timer(&timer, jiffies + msecs_to_jiffies(500));
}
static int __init timer_init(void)
{
/* 1.初始化定时器 */
init_timer(&timer);
/* 2.设置定时处理函数 */
timer.function = timer_function;
/* 3.设置500ms超时时间 */
timer.expires = jiffies + msecs_to_jiffies(500);
/* 4.启动定时器 */
add_timer(&timer);
return 0;
}
static void __exit timer_exit(void)
{
printk("exit\n");
/* 5.删除定时器 */
del_timer(&timer);
}
module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("panxiaoshuai");
结语
相信大家对内核定时器的使用基本有所了解,如果我的文章对你有所收获的话,可以点赞关注转发给你小伙伴们
作者潘小帅, 是一名Linux底层爱好者,不定期写写技术原创文章和分享职场面试文章,徒步,旅游,看电影的爱好,喜欢我的文章可以点赞收藏+关注,感谢你的支持,微信公众号【Linux随笔录】
参考文章:
https://zhuanlan.zhihu.com/p/533792421 https://blog.csdn.net/weixin_45309916/article/details/118584630 https://abcdxyzk.github.io/blog/2013/07/01/kernel-timer/ https://www.cnblogs.com/zhiminyu/p/17628488.html