內核定時器

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

 

發佈了65 篇原創文章 · 獲贊 39 · 訪問量 42萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章