第二十七节、内核定时器
本节用于介绍内核定时器
一、Linux内核定时器概念
不同于单片机定时器,Linux内核定时器是一种基于未来时间点的计时方式,以当前时刻为启动的时间点,以未来的某一时刻为终止点。比如,现在是10点5分,我要定时5分钟,那么定时就是10点5分+5分钟=10点10分。这个和咱们的手机闹钟很类似。比如你要定一个第二天早晨的8点的闹钟,就是当前时间定时到第二天早晨8点。
需要注意的是,内核定时器定时精度不高,不能作为高精度定时器使用。并且内核定时器并不是周期性运行的,超时以后就会自动关闭,因此如果想要实现周期性定时,那么就需要在定时处理函数中重新开启定时器。
二、Linux内核定时器基础知识
Linux内核使用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)(unsigned long); //定时处理函数
unsigned long data; //要传递给function函数的参数
u32 flags;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
在这个结构体中,有几个参数我们需要重点关注一下。
一个是expires到期时间,单位是节拍数。
等于定时的当前的时钟节拍计数(存储在系统的全局变量jiffies)+定时时长对应的时钟节拍数量。
那么我怎么把时间转换成节拍数量呢?
示例:从现在开始定时1秒:
内核中有一个宏HZ,表示一秒对应的时钟节拍数,那么我们就可以通过这个宏来把时间转换成节拍数。所以,定时1秒就是:expires = jiffies + 1*HZ。
HZ的值我们是可以设置的,也就是说一秒对应的时钟拍数我们是可以设置的,Linux内核会使用CONFIG_HZ来设置自己的系统时钟。
文件位置路径:include/asm-generic/param.h
# undef HZ
# define HZ CONFIG_HZ /* Internal kernel timer frequency */
# define USER_HZ 100 /* some user interfaces are */
# define CLOCKS_PER_SEC (USER_HZ) /* in "ticks" like times() */
#endif /* __ASM_GENERIC_PARAM_H */
宏HZ就是CONFIG_HZ,因此HZ=100,表示一秒的节拍数是100,们在编译 Linux内核的时候可以通过图形化界面设置系统节拍率,按照如下路径打开配置界面。
通过上图我们可以发现可选的系统节拍率为100Hz、200Hz、250Hz、300Hz、500Hz和1000Hz。默认是100Hz。
第二个我们需要关心的参数是function超时处理函数
这个并不是硬件中断服务程序,原型是void (*function)(unsigned long);
第三个参数是data,传递给超时处理函数的参数
它可以把一个变量的地址转换成unsigned long。
三、Linux内核定时器常用相关操作函数
<1>时间转换函数—— ms转换为时钟节拍函数
文件位置路径:include/linux/jiffies.h
static __always_inline unsigned long msecs_to_jiffies(const unsigned int m)
{
if (__builtin_constant_p(m)) {
if ((int)m < 0)
return MAX_JIFFY_OFFSET;
return _msecs_to_jiffies(m);
} else {
return __msecs_to_jiffies(m);
}
}
<2> 时间转换函数——us转换为时钟节拍函数
static __always_inline unsigned long usecs_to_jiffies(const unsigned int u)
{
if (__builtin_constant_p(u)) {
if (u > jiffies_to_usecs(MAX_JIFFY_OFFSET))
return MAX_JIFFY_OFFSET;
return _usecs_to_jiffies(u);
} else {
return __usecs_to_jiffies(u);
}
}
举例:
<1>定时10ms
计算:jiffies+msecs_to_jiffies(10)
<2>定时10us
计算:jiffies+usecs_to_jiffies(10)
<3> DEFINE_TIMER宏
作用:静态定义结构体变量并且初始化初始化function,expires,data成员。
文件位置路径:include/linux/timer.h
#define DEFINE_TIMER(_name, _function, _expires, _data) \
struct timer_list _name = \
TIMER_INITIALIZER(_function, _expires, _data)
//参数:
//_name:变量名
//_function:超时处理函数
//_expires:到点时间,一般在启动定时前需要重新初始化
//_data:传递给超时处理函数的参数
<4> add_timer函数
作用:add_timer函数用于向Linux内核注册定时器,使用add_timer 函数向内核注册定时器以后,定时器就会开始运行
文件位置路径:include/linux/timer.h
实际函数位置路径:kernel/time/timer.c
extern void add_timer(struct timer_list *timer);
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));
mod_timer(timer, timer->expires);
}
EXPORT_SYMBOL(add_timer);
//参数:
//timer:要注册的定时器。
<4> del_timer 函数
作用:del_timer 函数用于删除一个定时器,不管定时器有没有被激活,都可以使用此函数删除。在多处理器系统上,定时器可能会在其他的处理器上运行,因此在调用del_timer函数删除定时器之前要先等待其他处理器的定时处理器函数退出timer:要删除的定时器。
文件位置路径:include/linux/timer.h
实际函数位置路径:kernel/time/timer.c
extern int del_timer(struct timer_list * timer);
int del_timer(struct timer_list *timer)
{
struct timer_base *base;
unsigned long flags;
int ret = 0;
debug_assert_init(timer);
if (timer_pending(timer)) {
base = lock_timer_base(timer, &flags);
ret = detach_if_pending(timer, base, true);
raw_spin_unlock_irqrestore(&base->lock, flags);
}
return ret;
}
EXPORT_SYMBOL(del_timer);
//参数:
//timer:要删除的定时器。
//返回值
//0,定时器还没被激活
//1,定时器已经激活
<5> mod_timer函数
作用:mod_timer函数用于修改定时值,如果定时器还没有激活的话,mod_timer函数会激活定时器!
文件位置路径:include/linux/timer.h
实际函数位置路径:kernel/time/timer.c
extern int mod_timer(struct timer_list *timer, unsigned long expires);
int mod_timer(struct timer_list *timer, unsigned long expires)
{
return __mod_timer(timer, expires, false);
}
EXPORT_SYMBOL(mod_timer);
//参数:
//timer:要修改超时时间(定时值)的定时器。
//expires:修改后的超时时间。
//返回值
//0,调用mod timer函数前定时器未被激活
//1,调用mod timer函数前定时器已被激活
四、Linux内核定时器补充相关操作函数
<1> init_timer宏
作用:初始化定时器
文件位置路径:include/linux/timer.h
#define init_timer(timer) \
__init_timer((timer), 0)
//参数:
//timer:要初始化的定时器的名称。
<2> TIMER_INITIALIZER宏
作用:用于赋值定时器结构体的function、expires、data和base成员
文件位置路径:include/linux/timer.h
#define TIMER_INITIALIZER(_function, _expires, _data) \
__TIMER_INITIALIZER((_function), (_expires), (_data), 0)
//参数:
//_function:超时处理函数
//_expires:到点时间,一般在启动定时前需要重新初始化
//_data:传递给超时处理函数的参数
<3>setup_timer宏
作用:用于初始化定时器并赋值其成员
文件位置路径:include/linux/timer.h
#define setup_timer(timer, fn, data) \
__setup_timer((timer), (fn), (data), 0)
//参数:
//timer:要初始化的定时器的名称。
//fn:超时处理函数
//data:传递给超时处理函数的参数