转自:http://blog.chinaunix.net/u2/73528/showart_1131053.html
一、定义:
/include/linux/timer.h
struct timer_list { struct list_head entry; unsigned long expires; void (*function)(unsigned long); unsigned long data; struct tvec_t_base_s *base; #ifdef CONFIG_TIMER_STATS void *start_site; char start_comm[16]; int start_pid; #endif }; |
二、作用:
一个 timer_list 结构体的实例对应一个定时器,在 linux 设备驱动编程中,可以使用 timer_list 和基于它的一些操作 ( 函数 ) 来完成定时出发工作或者完成某周期性的事物。
三、个字段详解:
1 、 struct list_head entry;
定时器链表,用于存放软定时器,该链表根据定时器 expirex 字段的值将它们分组存放。
2 、 unsigned long expires;
定时器的到期时间,到达 expires 时间后,定时器将调用其成员函数 function ,其中将 data 字段作为 function 的参数。该字段表示的时间是以时间节拍为单位。例如如果你想定时一秒,则 expires=jiffies+HZ*1 。关于 jiffies 的详解见: http://blog.chinaunix.net/u2/73528/showart_1130865.html
3 、 void (*function)(unsigned long);
定时器处理函数,也就是说到达 expires 时间时, function 函数将被调用执行。起参数来自定时器的 data 字段。
4 、 unsigned long data;
在调用 function 函数时,该字段作为其参数被使用。
四、操作:
1 、定义及初始化:
(1)
struct timer_list timer;
void init_timer(struct timer_list *timer);
init_timer() 函数被定义在 kernel/timer.c 中,实际上是将 timer 的 entry 的 next 指针置为 NULL, 为 base 字段赋值。
(2)
struct timer_list timer;
timer=TIMER_INITIALIZER(function,expires,data);
采用这种初始化方式,必须首先先写好定时器处理函数 function. TIMER_INITIALIZER 宏的定义如下:
#define TIMER_INITIALIZER(_function, _expires, _data) { / .function = (_function), / .expires = (_expires), / .data = (_data), / .base = &boot_tvec_bases, / } |
其中 boot_tcec_bases 是在 kernel/timer 中定义的一个全局的 tvec_t_base_s 类型的变量。
(3)
DEFINE_TIMER(timer,function,expires,data);
定义并初始化定时器 timer, 相当于 (2). 其中 DEFINE_TIMER 宏的定义为:
#define DEFINE_TIMER(_name, _function, _expires, _data) / struct timer_list _name = / TIMER_INITIALIZER(_function, _expires, _data |
)
(4)
struct timer_list timer;
setup_timer(&timer);
等同于定义方式 (2) 和 (3) ,不过对 base 字段的赋值是调用了 init_timer() 函数。 setup_timer() 原型为:
static inline void setup_timer(struct timer_list * timer, void (*function)(unsigned long), unsigned long data) { timer->function = function; timer->data = data; init_timer(timer); } |
2 、注册定时器:
在定义并初始化了定时器之后,就要调用 add_timer() 函数来将该定时器注册到内核中,这样定时器才会工作。在注册之后,定时器就开始计时,在到达时间 expires 时,执行回调函数 function(->data) 。 add_timer() 函数的原型为:
<!--
@page { size: 21.59cm 27.94cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
static inline void add_timer(struct timer_list *timer) { BUG_ON(timer_pending(timer)); __mod_timer(timer, timer->expires); } |
3 、删除定时器:
int del_timer(struct timer_list *timer);
从内核中删除已经注册的定时器 timer 。如果该定时器是活动的,则返回 1 ,否则返回 0 。
int del_timer(struct timer_list *timer) { tvec_base_t *base; unsigned long flags; int ret = 0;
timer_stats_timer_clear_start_info(timer); if (timer_pending(timer)) { base = lock_timer_base(timer, &flags); if (timer_pending(timer)) { detach_timer(timer, 1); ret = 1; } spin_unlock_irqrestore(&base->lock, flags); }
return ret; } |
4 、修改定时器的定时时间:
int mod_timer(struct timer_list *timer, unsigned long expires) { BUG_ON(!timer->function);
timer_stats_timer_set_start_info(timer); /* * This is a common optimization triggered by the * networking code - if the timer is re-modified * to be the same thing then just return: */ if (timer->expires == expires && timer_pending(timer)) return 1;
return __mod_timer(timer, expires); } |
从代码可以看出,如果所给的要修改的时间等于定时器原来的时间并且定时器现在正处于活动状态,则不修改,返回 1 ,否则修改定时器时间,返回 0 。 mod_timer() 是一个非有效的更新处于活动状态的定时器的时间的方法,如果定时器处于非活动状态,则会激活定时器。在功能上, mod_timer() 等价于:
del_timer(timer);
timer->expires=expires;
add_timer(timer);
五、内核延时函数:
1 、短延时:
ndelay(unsigned long nsecs); /* 延时 nsecs 纳秒 */
udelay(unsigned long usecs); /* 延时 usecs 微秒 */
mdelay(unsigned long msecs); /* 延时 msecs 毫秒 */
此三个宏延时的本质是“忙等待”,也就是说在延时的过程中,并没有放弃 CPU ,而是根据 CPU 的频率进行一定次数的循环来达到延时的目的。三个宏最终都是将各自的参数(延时的时间)经过一定的换算调用 delay_loop() 函数来循环耗时达到延时, delay_loop() 如下:
static void delay_loop(unsigned long loops) { int d0;
__asm__ __volatile__( "/tjmp 1f/n" ".align 16/n" "1:/tjmp 2f/n" ".align 16/n" "2:/tdecl %0/n/tjns 2b" :"=&a" (d0) :"0" (loops)); } |
可以明显的看到每次自减 loops ,然后判断,如果为 0 ,则结束,否则跳到标号 2 处,形成循环。这就是所谓的“忙等待”。
2 、长延时:
在内核中,一个直观的延时的方法是将所要延迟的时间设置的当前的 jiffies 加上要延迟的时间,这样就可以简单的通过比较当前的 jiffies 和设置的时间来判断延时的时间时候到来。针对此方法,内核中提供了简单的宏用于判断延时是否完成。
time_after(jiffies,delay); /* 此刻如果还没有到达延时的时间,则返回真,否则返回 0*/
time_before(jiffies,delay);/* 如果延时还没有完成,则返回真,否则返回 0*/
其中 time_after 和 time_before 分别被定义为:
#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) |
在具体使用中也是将 time_after 或者 time_before 作为 while 循环的判断语句,进行忙等待延时。
3 、睡眠延时:
与忙等待延时相对的是睡眠延时,在延时的过程中,进程是处于睡眠状态,这意味着其他的任务可以在这是被调度执行,提高了 CPU 的有效利用率。在睡眠给定的时间后,任务又被重新调度执行。内核提供的睡眠延时函数是:
void msleep(unsigned int msecs) ;
unsigned long msleep_interruptible(unsigned int msecs) ;
则会两个函数的区别是调用 msleep() 函数进行睡眠延时的进程不能被信号打断,而调用 msleep_interruptible() 函数延时的进程可以被信号唤醒。一下给出 msleep() 函数的代码:
void msleep(unsigned int msecs) { unsigned long timeout = msecs_to_jiffies(msecs) + 1;
while (timeout) timeout = schedule_timeout_uninterruptible(timeout); } |
可以看出 msleep() 本质还是依靠 schedule_timeout_uninterruptible() 函数实现的。在每次被重新调度执行时,如果睡眠没有完成,则重新进入睡眠直到到达睡眠的时间。