内核定时器可以实现定时执行某个事务,一个典型的应用就是使用内核定时器实现轮训操作,因为定时器到时后可以在定时器函数里面重新定时启用该定时器,达到循环启用定时器的作用,这样可以实现定时轮训硬件
内核定时器基本上会在"软件中断"上下文中运行,所以定时器函数要注意以原子地运行;且定时器始终会在调度它的同一CPU上运行。另外需要遵守以下规则:
o 不允许访问用户空间;
o 不允许访问current指针;
o 不能执行休眠或调度。
所有处于进程上下文之外的程序都要遵守这些规则。
<include/linux/timer.h>
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;
unsigned long expires; // 定时器到时时间
struct tvec_base *base;
void (*function)(unsigned long); // 定时器到时后,被执行的回调函数
unsigned long data; // 传给回调函数的数据,因为定时器回调在中断上下文执行,所以数据一般以指针方式传入
int slack;
};
注意事项
内核定时器基本上会在"软件中断"上下文中运行,所以定时器函数要注意以原子地运行;且定时器始终会在调度它的同一CPU上运行。另外需要遵守以下规则:
o 不允许访问用户空间;
o 不允许访问current指针;
o 不能执行休眠或调度。
所有处于进程上下文之外的程序都要遵守这些规则。
内核提供两个API来判断中断上下文和原子上下文
#include <linux/hardirq.h>
int in_interrupt(); // 如果在中断上下文,则返回非零值
int in_atomic(); // 如果在原子上下文,则返回非零值
定时器应用api
#include <linux/timer.h>
void init_timer(struct timer_list *timer); // 初始化定时器
struct timer_list TIMER_INITIALIZER(_function, _expires, _data); // 宏初始化并指定相关成员值
void add_timer(struct timer_list *timer); // 向内核添加定时器
int del_timer(struct timer_list *timer); // 移除定时器
应用场景
struct device_regs *devreg = NULL; //定义一个用于表示设备寄存器的结构体指针
struct timer_list demo_timer; //定义一个内核定时器对象
//定义定时器函数,当定时器对象demo_timer中expires成员指定的时间到期后,该函数将被调用
static void demo_timer_func (unsigned long data)
{
//在定时器函数中重新启动定时器以实现轮询的目的
demo_timer.expires = jiffies + HZ;
add_timer(&demo_timer);
//定时器函数将data参数通过类型转换获得设备寄存器的结构体指针
struct device_regs *preg = (struct device_regs *) data;
//定时器函数此后将会读取设备状态
...
}
//用于打开设备的函数实现
static int demo_dev_open(…)
{
...
//分配设备寄存器结构体的指针变量,最好放在模块初始化函数中…
devreg = kmalloc(sizeof(struct device_regs), GFP_KERNEL);
...
init_timer(&demo_timer); //调用内核函数init_timer来初始化定时器对象demo_timer
demo_timer.expires = jiffies + HZ; //设定定时器到期时间点,从现在开始的1秒钟
demo_timer.data = (unsigned long) devreg; //将设备寄存器指针地址作为参数
demo_timer.function = &demo_timer_func;
add_timer(&demo_timer);
...
}
//用于关闭设备的函数实现
static int demo_dev_ release(…)
{
...
del_timer_sync(&demo_timer); //删除定时器对象
...
}