Linux 内核定时器及使用方法

Linux 内核定时器及使用方法


一.度量时间差


时钟中断是由系统的定时硬件以周期性的时间间隔产生,这个间隔(即频率)由内核根据HZ来确定,HZ是一个与体系结构无关的常量(定义在),可配置(50-1200),在X86平台,默认值为1000.HZ的含义是系统每秒钟产生的时钟中断的次数.


每当时钟中断发生时,全局变量jiffies(一个32位的unsigned long 变量,定义在)就加1,因此jiffies记录了字linux系统启动后时钟中断发生的次数.驱动程序常利用jiffies来计算不同事件间的时间间隔.


内核提供了一组宏用来比较时间量:


#include
int time_after(unsigned long a, unsigned long b);
int time_before(unsigned long a, unsigned long b);
int time_after_eq(unsigned long a, unsigned long b);
int time_after_eq(unsigned long a, unsigned long b);




这几个宏可以理解为 a 宏名 b? 1:0.






获取当前时间:






#include
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
};
void do_gettimeofday(struct timeval *tv)










二.内核定时器


内核定时器用于控制某个函数(定时器处理函数)在未来的某个特定时间执行.内核定时器注册的处理函数只执行一次.处理过后即失效.


当内核定时器被调度运行时,几乎可以肯定其不会在注册这些函数的进程正在执行时.相反,它会异步的执行.这种异步类似于硬件中断发生时的情景.实际上,内核定时器是被"软件中断"调度运行的.因此,其运行于原子上下文中.这点和tasklet很类似.处于原子上下文的进程有一些运行时的限制:


1. 不能访问用户空间.因为没有进程上下文.无法与特定的进程与用户关联


2. 不能执行调度或休眠.


3. Current指针在原子模式下无意义.


内核定时器被组织成双向链表,使用struct timer_list结构描述.


struct time_list{


unsigned long expires; //超时的jiffies值
void(*function)(unsigned long) ; //注册的定时器处理函数
unsigned long data; //定时器处理函数的参数
}




这3个字段表示,当jiffies等于expires时,内核会调度function函数运行.data是传递给function的参数的指针,如果function函数需要不止一个参数,那么可以将这几个参数组成一个结构体,并将结构体的指针赋值给data.




三.管理定时器的接口


void init_timer(struct time_list *timer);


初始化定时器队列结构.timer_list结构在使用前必须初始化,这是要保证结构体中其他的成员能正确赋值.


void add_timer(struct time_list *timer);


启动定时器.


int del_timer(struct time_list *timer);


在定时器超时前将定时器删除.当定时器超时后,系统会自动将其删除.




四.内核定时器的使用方法


初始化


在使用 struct timer_list 之前,需要初始化该数据结构,确保所有的字段都被正确地设置。初始化有两种方法。


方法一:


DEFINE_TIMER(timer_name, function_name, expires_value, data);


该宏会静态创建一个名叫 timer_name 内核定时器,并初始化其 function, expires, name 和 base 字段。


 


方法二:


struct timer_list mytimer;


setup_timer(&mytimer, (*function)(unsigned long), unsigned long data);


mytimer.expires = jiffies + 5*HZ;


方法3:


struct timer_list mytimer;


init_timer(&mytimer);    


  mytimer ->timer.expires = jiffies + 5*HZ;


  mytimer ->timer.data = (unsigned long) dev;


  mytimer ->timer.function = &corkscrew_timer; /* timer handler */


通过init_timer()动态地定义一个定时器,此后,将处理函数的地址和参数绑定给一个timer_list,


注意,无论用哪种方法初始化,其本质都只是给字段赋值,所以只要在运行 add_timer() 之前,expires, function 和 data 字段都可以直接再修改。


关于上面这些宏和函数的定义,参见 include/linux/timer.h。


 


注册


定时器要生效,还必须被连接到内核专门的链表中,这可以通过 add_timer(struct timer_list *timer) 来实现。


 


重新注册


要修改一个定时器的调度时间,可以通过调用  mod_timer(struct timer_list *timer, unsigned long expires)。mod_timer() 会重新注册定时器到内核,而不管定时器函数是否被运行过。


 


注销


注销一个定时器,可以通过 del_timer(struct timer_list *timer) 或 del_timer_sync(struct timer_list *timer)。其中 del_timer_sync 是用在 SMP 系统上的(在非SMP系统上,它等于del_timer),当要被注销的定时器函数正在另一个 cpu 上运行时,del_timer_sync() 会等待其运行完,所以这个函数会休眠。另外还应避免它和被调度的函数争用同一个锁。对于一个已经被运行过且没有重新注册自己的定时器而言,注销函数其实也没什么事可做。


 


int timer_pending(const struct timer_list *timer)


这个函数用来判断一个定时器是否被添加到了内核链表中以等待被调度运行。注意,当一个定时器函数即将要被运行前,内核会把相应的定时器从内核链表中删除(相当于注销)



当删除定时器时,必须小心一个潜在的竞争条件。当del_timer()返回后,可以保证的只是: 定时器不会再被激活(也就是,将来不会执行),但是在多处理机器上定时器中断可能已经在其他处理器上运行了,所以删除定时器时需要等待可能在其他处理器上 运行的定时器处理程序都退出,这时就要使用del_timer_sync()函数执行删除工作:
del_timer_sync(&my_timer);

和del_timer()函数不同,del_timer_sync()函数不能在中断上下文中使用.


特别注意:

      特别要注意顺序:提供两个顺序参考:

     一:

  1.         struct timer_list mytimer;
  2.        init_timer(&mytimer);    
  3.        mytimer.function=mytimer_function;
  4.        mytimer.expires=jiffies+HZ/100
  5.        add_timer(&mytimer);
          
      mytimer.data的值可以在第五条之前设置,也可以不设置;看自己用不用传递参数。      注意jiffies这个的数值是一直在变的所以第四天和第五条不要离太远!!!!

再给一个顺序

 二:

  1.       struct timer_list mytimer;
  2.        init_timer(&mytimer);    
  3.       mytimer.function=mytimer_function;
  4.       add_timer(&mytimer);          //没有设置expires字段。默认为0,立即执行mytimer_function;
  5.        。。。。。。
  6.       。。。。。。
  7.       mod_timer(&mytimer, jiffies+HZ/100);  //设定expires字段。重启定时器;
       注意 jiffies这个的数值是一直在变的所以




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值