一、内核定时器介绍
内核定时器是内核用来控制在未某个时间点(基于jiffies(节拍总数))调度执行某个函数的一种机制,相关函数位于<linux/timer.h> 和 kernel/timer.c 文件中。
当内核定时器定时时间到达时,会进入用户指定的函数,相当于软中断。内核定时器注册开启后,运行一次就不会再运行(相当于自动注销),我们可以重新设置定时器的超时时间,让定时器重复运行。
每当时钟中断发生时,全局变量jiffies(一个32位的unsigned long 变量)就加1,因此jiffies记录了linux系统启动后时钟中断发生的次数,驱动程序常利用jiffies来计算不同事件间的时间间隔。内核每秒钟将jiffies变量增加HZ次。因此,对于HZ值为100的系统,jiffy+1等于隔了10ms,而对于HZ为1000 的系统, jiffy+1仅为1ms。
注意: jiffies 变量不能被修改,修改会出现错误。
二、内核定时器相关API
1、内核定时器结构体
struct timer_list {
/*
* All fields that change during normal runtime grouped to the same cacheline
*/
struct hlist_node entry;
unsigned long expires;//设置超时时间,jiffies+xxx
void (*function)(unsigned long);//定时时间到后处理的函数
unsigned long data;//传递给上面函数的参数
u32 flags;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
2、初始化定时器
//初始化定时器宏定义
#define init_timer(timer)//timer为定时器结构体指针 \
__init_timer((timer), 0)
//初始化定时器具体实现
#define __init_timer(_timer, _flags) \
do { \
static struct lock_class_key __key; \
init_timer_key((_timer), (_flags), #_timer, &__key); \
} while (0)
3、初始化定时器并赋值
//初始化定时器并赋值宏定义
#define setup_timer(timer, fn, data) \
__setup_timer((timer), (fn), (data), 0)
//初始化定时器并赋值具体实现
#define __setup_timer(_timer, _fn, _data, _flags) \
do { \
__init_timer((_timer), (_flags)); \
(_timer)->function = (_fn); //定时到处理函数 \
(_timer)->data = (_data);//传递给处理函数的参数 \
} while (0)
4、用宏来初始化定时器
//宏定义
#define TIMER_INITIALIZER(_function, _expires, _data) \
__TIMER_INITIALIZER((_function), (_expires), (_data), 0)
//实现部分
#define __TIMER_INITIALIZER(_function, _expires, _data, _flags) { \
.entry = { .next = TIMER_ENTRY_STATIC }, \
.function = (_function), \
.expires = (_expires), \
.data = (_data), \
.flags = (_flags), \
__TIMER_LOCKDEP_MAP_INITIALIZER( \
__FILE__ ":" __stringify(__LINE__)) \
}
5、用宏来初始化定时器并赋值
//宏定义
#define DEFINE_TIMER(_name, _function, _expires, _data) \
struct timer_list _name =//定义了变量 \
TIMER_INITIALIZER(_function, _expires, _data)
//实现部分
#define TIMER_INITIALIZER(_function, _expires, _data) \
__TIMER_INITIALIZER((_function), (_expires), (_data), 0)
#define __TIMER_INITIALIZER(_function, _expires, _data, _flags) { \
.entry = { .next = TIMER_ENTRY_STATIC }, \
.function = (_function), \
.expires = (_expires), \
.data = (_data), \
.flags = (_flags), \
__TIMER_LOCKDEP_MAP_INITIALIZER( \
__FILE__ ":" __stringify(__LINE__)) \
}
6、添加定时器
void add_timer(struct timer_list *timer)//参数为定时器结构体指针
{
BUG_ON(timer_pending(timer));
mod_timer(timer, timer->expires);//就是设置超时时间,如果只设置一次,执行完毕就结束了
}
7、修改定时器超时时间
在内核定时器的介绍中我们说到,内核定时器注册开启后,运行一次就不会再运行(相当于自动注销),我们可以重新设置定时器的超时时间,让定时器重复运行。
int mod_timer(struct timer_list *timer, unsigned long expires)
//变量分别为定时器结构体指针和超时时间
{
return __mod_timer(timer, expires, false);//具体细节看内核源码
}
8、关闭定时器
/*
函数功能:删除定时器
*/
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;
}
/*
函数功能:删除定时器,和上面函数不同的是,该函数用在多处理器(SMP)时,如何有其他的处理器在使用
定时器时,该函数会睡眠直到使用完毕再将定时器删除。
*/
int del_timer_sync(struct timer_list *timer)
{
#ifdef CONFIG_LOCKDEP
unsigned long flags;
/*
* If lockdep gives a backtrace here, please reference
* the synchronization rules above.
*/
local_irq_save(flags);
lock_map_acquire(&timer->lockdep_map);
lock_map_release(&timer->lockdep_map);
local_irq_restore(flags);
#endif
/*
* don't use it in hardirq context, because it
* could lead to deadlock.
*/
WARN_ON(in_irq() && !(timer->flags & TIMER_IRQSAFE));
for (;;) {
int ret = try_to_del_timer_sync(timer);
if (ret >= 0)
return ret;
cpu_relax();
}
9、转换时间
/*
函数功能:微秒转节拍(usecs_to_jiffies)
*/
static __always_inline unsigned long usecs_to_jiffies(const unsigned int u)
//具体实现如下
static inline unsigned long _usecs_to_jiffies(const unsigned int u)
{
return (u + (USEC_PER_SEC / HZ) - 1) / (USEC_PER_SEC / HZ);
}
#else
static inline unsigned long _usecs_to_jiffies(const unsigned int u)
{
return (USEC_TO_HZ_MUL32 * u + USEC_TO_HZ_ADJ32)
>> USEC_TO_HZ_SHR32;
}
/*
函数功能:毫秒转节拍(msecs_to_jiffies)
*/
static __always_inline unsigned long msecs_to_jiffies(const unsigned int m)
三、内核定时器的使用步骤
1、定义时间结构体变量。
2、编写定时器时间到处理函数。
3、初始化定时器,并赋值。
4、删除定时器
技巧:如果定时器想一直开启,可以将修改定时器时间放在定时器时间到处理函数中。
来源:CSDN
作者:首富•拖拉斯基
链接:https://blog.csdn.net/weixin_45983581/article/details/104576896