一、定義:
/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()函數的原型爲:
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()函數實現的。在每次被重新調度執行時,如果睡眠沒有完成,則重新進入睡眠直到到達睡眠的時間。
====