内核定时器的使用
前言:
· 内核定时器是一个精度低的定时器(一般都是毫秒级别的),他是基于内核中的 jiffies 变量来实现的
· 软件意义上的定时器最终都是依赖硬件定时器来实现的
· 内核定时器的精低精度的定时器,一般用做看门狗等对时间精度不敏感的情况下使用
· 假如要定时1个 jiffies 的时间长度,内核定时器是无法做到准确定时的,因为内核定时器可能是在上一个jiffies刚走完一定的时间,又还没到下一个 jiffies 时就开始定时,这样会导致实际的定时时间小于一个 jiffies 的长度
1 struct timer_list
· timer_list 是将 expires 与 jiffies 进行比较实现的,但 jiffies 大于或等于 expires 时,即定时器到期
#include <linux/timer.h>
/* 老版 Linux 中的定义 */
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct hlist_node entry; /* 哈希表,kernel 用于管理 timer_list */
unsigned long expires; /* 定时器到时时间,单位是节拍数 */
/* 当定时器满期后,function 函数将被执行 */
void (*function)(struct timer_list *);
u32 flags; /* 时钟标志位 */
/* 用于在内核锁的情况下跟踪计时器的使用情况 */
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
/* 当今主流的 Linux 中的定义 */
struct timer_list {
struct hlist_node entry; /* 哈希表,kernel 用于管理 timer_list */
unsigned long expires; /* 定时器到时时间,单位是节拍数 */
/* 当定时器满期后,function 函数将被执行 */
void (*function)(unsigned long);
unsigned long data; /* 回调函数的参数 */
u32 flags; /* 时钟标志位 */
int slack;
};
· u32 flags (也定义在 linux/timer.h 中)
· TIMER_CPUMASK:表示定时器可以在哪些 CPU 上运行
· TIMER_MIGRATING:表示定时器正在迁移中
· TIMER_BASEMASK:是 TIMER_CPUMASK 和 TIMER_MIGRATING 的位或运算结果
· TIMER_DEFERRABLE:表示该定时器是 deferrable 的,即不会阻塞 CPU,而是在 CPU 空闲时执行
· TIMER_PINNED:表示该定时器是固定在 CPU 上的,不能移动到其他 CPU 上。
· TIMER_IRQSAFE:表示该定时器是 irqsafe 的,即在执行回调函数时不会影响其他中断的处理
· TIMER_ARRAYSHIFT:表示 TIMER_ONESHOT、TIMER_IRQSAFE 和 TIMER_PINNED 这三个位的位置
· TIMER_ARRAYMASK:是 TIMER_CPUMASK、TIMER_ARRAYSHIFT 和 TIMER_BASEMASK 的按位与运算结果
· TIMER_TRACE_FLAGMASK:是 TIMER_MIGRATING、TIMER_DEFERRABLE、TIMER_PINNED 和 TIMER_IRQSAFE 的按位或运算结果,用于标记定时器的一些属性
2 init_timer 初始化定时器
· 将 timer_list 的 entry 的 next 指针赋值为 NULL,并给 base 指针赋值
#include <linux/timer.h>
// init_timer 是一个宏定义,等价于下面的函数形式
void init_timer(struct timer_list *timer);
· struct timer_list *timer:要初始化的 struct timer_list 对象
3 add_timer 增加定时器
· 将对应的定时器加入到内核动态定时器链表中,注册内核定时器
#include <linux/timer.h>
void add_timer(struct timer_list *timer);
· struct timer_list *timer:要增加的 struct timer_list 对象
4 删除定时器
4.1 del_timer 直接删除定时器
#include <linux/timer.h>
int del_timer(struct timer_list *timer);
· int:执行成功返回 0,失败返回非零
· struct timer_list *timer:要删除的 struct timer_list 对象
4.2 del_timer_sync 同步后删除定时器
· del_timer_sync 是 del_timer 的同步版本,在删除一个定时器时需要等待其被处理完后,再进行删除
· 有休眠等待,不能用于中断上下文
#include <linux/timer.h>
int del_timer_sync(struct timer_list *timer);
· int:执行成功返回 0,失败返回非零
· struct timer_list *timer:要增加的 struct timer_list 对象
5 mod_timer 修改定时器的 expire
· 用于修改定时器的到期时间,在新的被传入的 expires 到来后才会执行定时器函数
#include <linux/timer.h>
int mod_timer(struct timer_list *timer, unsigned long expires);
· int:执行成功返回 0,失败返回非零
· struct timer_list *timer:要操作的 struct timer_list 对象
· unsigned long expires:将 expires 修改成的值
6 具体使用案例说明
/* 设备结构体 */
struct try_dev {
struct cdev cdev;
...
timer_list *xxx_timer; // 设备要使用的内核定时器指针
}
/* 在初始化设备结构体函数中 */
xxx xxx_init(xxx){
try_dev .timer = (struct timer_list *)kmalloc(sizeof(struct timer_list), GFP_KERNEL);
if(try_dev .timer == NULL) {
pr_err("struct timer_list kmalloc error");
} else {
memset(try_dev .timer, 0, sizeof(struct timer_list));
init_timer(try_dev.timer);
/* 绑定定时器的处理函数 */
try_dev.timer->function = timer_handle;
/* 传入设备结构体的地址给定时器处理函数 */
try_dev.timer->data = (unsigned long)&try_dev;
}
}
/* 需要使用到定时器时的某函数 */
xxx xxx_function(xxx){
...
try_dev.timer->expires = jiffier + delay;
add_timer(try_dev.timer);
...
}
/* 需要删除定时器时的某函数 */
xxx xxx_function(xxx){
...
del_timer(try_dev.timer);
...
}
/* 内核定时器的服务处理函数 */
static void timer_handle(unsigned long arg) {
struct try_dev *p = (struct try_dev *)arg;
...
/* 若需循环使用定时器 */
p->timer->expires = jiffies + delay;
add_timer(p->timer);
...
}