Linux内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,其实现位于 <Linux/timer.h> 和 kernel/timer.c 文件中。无论是从单片机还是到后面的多任务系统,还是RTOS到Linux,都需要用到定时器。
首先我们要知道被调度的函数肯定是异步执行的,它类似于一种“软件中断”,而且是处于非进程的上下文中,所以调度函数必须遵守以下规则:
1) 没有 current 指针、不允许访问用户空间。因为没有进程上下文,相关代码和被中断的进程没有任何联系。
2) 不能执行休眠(或可能引起休眠的函数)和调度。
3) 任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件。
而内核定时器的调度函数运行过一次后就不会再被运行了(相当于自动注销),但可以通过在被调度的函数中重新调度自己来周期运行。
在SMP系统中,调度函数总是在注册它的同一CPU上运行,以尽可能获得缓存的局域性。
1.Linux内核定时器的数据结构:
struct timer_list {
struct list_head entry;
unsigned long expires;
void (*function)(unsigned long);
unsigned long data;
struct tvec_base *base;
/* ... */
};
其中 expires 字段表示期望定时器执行的 jiffies 值,到达该 jiffies 值时,将调用 function 函数,并传递 data 作为参数。当一个定时器被注册到内核之后,entry 字段用来连接该定时器到一个内核链表中。base 字段是内核内部实现所用的。
2.jiffies
全局变量jiffies用来记录自系统启动以来产生的节拍的总数。启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值。一秒内时钟中断的次数等于Hz,所以jiffies一秒内增加的值也就是Hz。
HZ:这个可以认为是一个频率,一秒内系统时钟中断的次数
Jiffies/HZ:我想知道从系统开机到现在系统运行了多少秒,可以这样计算 jiffies类型为无符号长整型(unsigned long)
当jiffies的值超过它的最大存放范围后就会发生溢出。对于32位无符号长整型,最大取值为(2^32)-1,即429496795。如果节拍计数达到了最大值后还要继续增加,它的值就会回绕到0。
3.使用定时器步骤
struct timer_list my_timer_list;//定义一个定时器,可以把它放在你的设备结构中init_timer(&my_timer_list);//初始化一个定时器
my_timer_list.expire=jiffies+HZ;//定时器1s后运行服务程序my_timer_list.function=timer_function;//定时器服务函数add_timer(&my_timer_list);//添加定时器
void timer_function(unsigned long);//写定时器服务函数del_timer(&my_timer_list);//当定时器不再需要时删除定时器del_timer_sync(&my_timer_list);//基本和del_timer一样,比较适合在多核处理器使用,一般推荐使用del_timer_sync
4.实例分析#include
#include
#include
#include /*timer*/
#include /*jiffies*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Qifa");
MODULE_DESCRIPTION("Timer Module");
MODULE_ALIAS("timer module");
struct timer_list timer;
void timer_function(int para)
{
static int i = 0;
printk("#########%d########Timer Expired and para is %d !!\n",i++,para);
printk("jiffies:%lu jiffies/Hz: %lu HZ:%d\n",jiffies,(jiffies/HZ),HZ);
mod_timer(&timer,jiffies+(2*HZ));
}
int timer_init(void)
{
init_timer(&timer);
timer.data = 5;
timer.expires = jiffies + (1 * HZ);
timer.function = timer_function;
add_timer(&timer);
return 0;
}
void timer_exit(void)
{
del_timer( &timer );
}
module_init(timer_init);
module_exit(timer_exit);
使用定时器的目的无外乎是为了周期性的执行某一任务,或者是到了一个指定时间去执行某一个任务。只要掌握了这一关键点,就能更好地理解定时器的工作机制,Linux内核定时器也是如此。在本站的Linux教程里还有关于Linux中其他类型的定时器的介绍,感兴趣的小伙伴可以深入学习一下。
你适合学Java吗?4大专业测评方法
代码逻辑 吸收能力 技术学习能力 综合素质
先测评确定适合在学习