二. Linux 内核定时器基础知识
Linux 内 核 使 用 timer_list 结 构 体 表 示 内 核 定 时 器 ,timer_list 定 义 在 文 件 include/linux/timer.h 中,定义如下:
struct timer_list {
struct list_head entry;
unsigned long expires; /* 定时器超时时间,不是时长,单位是节拍数 */
struct tvec_base *base;
void (*function)(unsigned long); /* 定时处理函数 */
unsigned long data; /* 要传递给 function 函数的参数 */
int slack;
};
在这个结构体中,有几个参数需要重点关注。 一是 expires 到期时间,单位是节拍数。 等于定时的当前的时钟节拍计数(存储在系统的全局变量 jiffies)+定时时长对应的时钟节拍数量。
那么怎么把时间转换成节拍数量呢?
示例:从现在开始定时 1 秒:内核中有一个宏 HZ, 表示一秒对应的时钟节拍数,那么就可以通过这个宏来把时间转换成节拍数。 所以,定时 1 秒就是:expires = jiffies + 1*HZ 。 HZ 的值是可以设置的,也就是说一秒对应的时钟拍数是可以设置的, Linux 内核会使用CONFIG_HZ 来设置系统时钟 。
打 开 文 件 include/asm generic/param.h , 有如下内容:
# undef HZ
# define HZ CONFIG_HZ
# define USER_HZ 100宏
HZ 就是 CONFIG_HZ,因此 HZ=100,表示一秒的节拍数是 100 , 在编译 Linux 内核的时候可以通过图形化界面设置系统节拍率,按照如下路径打开配置界面。通过上图可以发现可选的系统节拍率为 100Hz 、 200Hz 、 250Hz 、 300Hz 、 500Hz 和 1000Hz 。 默认是 100Hz 。
第二个需要关心的参数是 function 超时处理函数,这个并不是硬件中断服务程序。 原型:void function(unsigned long data);
第三个参数是 data 传递给超时处理函数的参数,可以把一个变量的地址转换成 unsigned long 。
1、时间转换函数
<1>ms 转换为时钟节拍函数
unsigned long msecs_to_jiffies(const unsigned int m)
<2>us 转换为时钟节拍函数
unsigned long usecs_to_jiffies(const unsigned int u)
举例:
<1> 定时10ms
计算: jiffies +msecs_to_jiffies(10)
<2> 定时10us
计算: jiffies +usecs_to_jiffies(10)
<2> 宏 DEFINE_TIMER
原型: #define DEFINE_TIMER(_name, _function, _expires, _data)
作用:静态定义结构体变量并且初始化初始化 function, expires, data 成员。
参数: _name 变量名;
_function 超时处理函数 ;
_data 传递给超时处理函数的参数 _expires 到点时间,一般在启动定时前需要重新初始化。
<3>add_timer 函数
原型: void add_timer(struct timer_list *timer)
作用: add_timer函数用于向Linux 内核注册定时器,使用 add_timer 函数向内核注册定时器以后,定时器就会开始运行
参数:
timer : 要注册的定时器。
<4>del_timer 函数
原型: int del_timer(struct timer_list * timer)
作用: del_timer 函数用于删除一个定时器,不管定时器有没有被激活,都可以使用此函数删除。 在多处理器系统上,定时器可能会在其他的处理器上运行,因此在调用 del_timer 函数 删除定时器之前要先等待其他处理器的定时处理器函数退出
timer : 要删除的定时器。
返回值: 0 , 定时器还没被激活; 1 , 定时器已经激活。
<5>mod_timer 函数
函数原型: int mod_timer(struct timer_list *timer, unsigned long expires)
作用: mod_timer 函数用于修改定时值,如果定时器还没有激活的话, mod_timer 函数会 激活定时器!
参数:
timer : 要修改超时时间 ( 定时值 ) 的定时器。
expires : 修改后的超时时间。
返回值: 0 , 调用 mod_timer 函数前定时器未被激活; 1 , 调用 mod_timer 函数前定时器已被激活。
每隔1s,打印一句话。
定时器初始化:
#include
DEFINE_TIMER(test_timer,timer_function,0,0);
static int hello_init(void)
{
printk("hello world\n");
test_timer.expires = jiffies + 1*HZ;
//jiffies相当于手机的当前时刻!!!
add_timer(&test_timer);//启动定时器
return 0;
}
超时处理函数,让超时之后继续加载:
static void timer_function(unsigned long data)
{
printk("This is timer function\n");
//重新开启定时器
mod_timer(&test_timer,jiffies + 1*HZ);
}
#include
DEFINE_TIMER(test_timer,timer_function,0,0);
static void timer_function(unsigned long data)
{
//超时时间处理函数
}
中断处理函数:
irq_handler_t test_key(int irq, void *args)//中断处理函数
{
test_timer.expires = jiffies + msecs_to_jiffies(20) ;
//jiffies相当于手机的当前时刻!!!
add_timer(&test_timer);//启动定时器
return IRQ_HANDLED;
}