linux内核定时器

来源:互联网 发布:网络买彩票 编辑:程序博客网 时间:2024/05/29 07:38
一、几个概念

1、节拍率:HZ
    系统定时器频率(节拍率)是通过静态预处理定义的,也就是HZ(赫兹),在系统启动时按照HZ值对硬件进行设置。体系结构或机器不同,HZ值都有可能不同。
    内核在<asm/param.h>文件中定义了这个值。
假设一个进程的时间片只剩下2ms了,此时调度程序又要求抢占该进程,然后去运行一个新进程,然而该抢占不会在下一个时钟中断到来前发生。对于频率为100HZ的时钟来说,最坏要在10ms后,当下一个时钟中断到来时才能进行抢占。

2、jiffies
    全局变量jiffies用来记录自系统启动以来产生的节拍的总数。启动时,内核将改变量初始化为0,此后,每次时钟中断处理程序就会增加该变量值,因为一秒内时钟中断次数等于HZ,所以jiffies一秒内增加的值也就为HZ。系统运行的时间以秒为单位计算,就等于jiffies/HZ。
    jiffies定义于文件<linux/jiffies.h>中:

extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;


3、jiffies的回绕
    对于32位无符号长整形,最大取值为2的32次方减1,所以在溢出前,定时器节拍计数最大为4294967295,如果节拍计数达到了最大计数后还要继续增加的话,它的值会回绕(wrap around)到0。
    在<linux/jiffies.h>中定义了几个宏解决回绕的问题:
#define time_after(a,b) \
 (typecheck(unsigned long, a) && \
  typecheck(unsigned long, b) && \
  ((long)(b) - (long)(a) < 0))
#define time_before(a,b) time_after(b,a)
#define time_after_eq(a,b) \
 (typecheck(unsigned long, a) && \
  typecheck(unsigned long, b) && \
  ((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)


二、内核定时器

1、使用定时器
    定时器由结构timer_list表示,定义在头文件<linux/timer.h>
 1 struct timer_list { 2  /* 3   * All fields that change during normal runtime grouped to the 4   * same cacheline 5   */ 6  struct list_head entry; 7  unsigned long expires; 8  struct tvec_base *base; 9 10  void (*function)(unsigned long);11  unsigned long data;12 13  int slack;14 15 #ifdef CONFIG_TIMER_STATS16  int start_pid;17  void *start_site;18  char start_comm[16];19 #endif20 #ifdef CONFIG_LOCKDEP21  struct lockdep_map lockdep_map;22 #endif23 };
使用定时器不需要深入了解该数据结构,内核提供了一组接口来简化操作。所有这些接口都声明在文件<linux/timer.h>中,大多数接口在文件kernel/timer.c中获得实现。
创建定时器时需要先定义它:
struct timer_list my_timer;
初始化:
init_timer(&my_timer);
填充结构体:
my_timer.expires = jiffies + delay;  //定时器超时时的节拍数
my_timer.data = 0;                   //给定时器处理函数传入0值
my_timer.function = my_function;     //定时器超时时调用的函数

my_timer.expires表示超时时间,它是以节拍为单位的绝对计数值。如果当前jiffies计数等于或大于my_timer.expires,那么my_timer.function指向的处理函数就会开始执行,另外该函数还要使用长整型参数my_timer.data,data参数的使用可以利用同一个处理函数注册多个定时器,只需通过该参数就能区别对待他们。下面是它的函数原型。
void my_function(unsigned long data);
激活定时器:
add_timer(&my_timer);
定时器也有可能延误到下一个时钟节拍才能运行,所以不能用定时器来实现任何硬实时任务。
下面的函数mod_timer用来修改定时器超时时间。mod_timer也可操作那些已经初始化,但是还没被激活的定时器,如果定时器没被激活mod_timer会激活它,如果调用的定时器未被激活,该函数返回0,否则返回1。
mod_timer(&my_timer, jiffies + new_delay);
如果需要在定时器超时前停止定时器,可以使用del_timer()函数:
如果定时器还未被激活该函数返回0,否则返回1。不需要为已经超时的定时器使用该函数,因为它们会自动删除。
del_timer(&my_timer);
当删除定时器时,必须注意一个潜在的竞争条件,当del_timer()返回后,可以保证的只有定时器不会再被激活,但是在多处理器机器上定时器中断可能已经在其他处理器上运行了,所以删除定时器时需要等待可能在其他处理器上运行的定时器处理程序都退出,这时就要使用del_timer_sync()函数执行删除工作:
del_timer_sync(&my_timer);
和del_timer()函数不同,del_timer_sync()函数不能在中断上下文中使用。
22 0