Linux 2.6 内核定时器
一、定義:
/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 }; |
二、作用:
一個(gè)timer_list結(jié)構(gòu)體的實(shí)例對應(yīng)一個(gè)定時(shí)器,在linux設(shè)備驅(qū)動(dòng)編程中,可以使用timer_list和基于它的一些操作(函數(shù))來完成定時(shí)出發(fā)工作或者完成某周期性的事物。
三、個(gè)字段詳解:
1、struct list_head entry;
定時(shí)器鏈表,用于存放軟定時(shí)器,該鏈表根據(jù)定時(shí)器expirex字段的值將它們分組存放。
2、unsigned long expires;
定時(shí)器的到期時(shí)間,到達(dá)expires時(shí)間后,定時(shí)器將調(diào)用其成員函數(shù)function,其中將data字段作為function的參數(shù)。該字段表示的時(shí)間是以時(shí)間節(jié)拍為單位。例如如果你想定時(shí)一秒,則expires=jiffies+HZ*1。關(guān)于jiffies的詳解見:http://blog.chinaunix.net/u2/73528/showart_1130865.html
3、void (*function)(unsigned long);
定時(shí)器處理函數(shù),也就是說到達(dá)expires時(shí)間時(shí),function函數(shù)將被調(diào)用執(zhí)行。起參數(shù)來自定時(shí)器的data字段。
4、unsigned long data;
在調(diào)用function函數(shù)時(shí),該字段作為其參數(shù)被使用。
四、操作:
1、定義及初始化:
(1)
struct timer_list timer;
void init_timer(struct timer_list *timer);
init_timer()函數(shù)被定義在kernel/timer.c中,實(shí)際上是將timer的entry的next指針置為NULL,為base字段賦值。
(2)
struct timer_list timer;
timer=TIMER_INITIALIZER(function,expires,data);
采用這種初始化方式,必須首先先寫好定時(shí)器處理函數(shù)function. TIMER_INITIALIZER宏的定義如下:
?
| #define TIMER_INITIALIZER(_function, _expires, _data) { / .function = (_function), / .expires = (_expires), / .data = (_data), / .base = &boot_tvec_bases, / } |
其中boot_tcec_bases是在kernel/timer中定義的一個(gè)全局的tvec_t_base_s類型的變量。
(3)
DEFINE_TIMER(timer,function,expires,data);
定義并初始化定時(shí)器timer,相當(dāng)于(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字段的賦值是調(diào)用了init_timer()函數(shù)。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、注冊定時(shí)器:
在定義并初始化了定時(shí)器之后,就要調(diào)用add_timer()函數(shù)來將該定時(shí)器注冊到內(nèi)核中,這樣定時(shí)器才會(huì)工作。在注冊之后,定時(shí)器就開始計(jì)時(shí),在到達(dá)時(shí)間expires時(shí),執(zhí)行回調(diào)函數(shù)function(->data)。add_timer()函數(shù)的原型為:
?
| static inline void add_timer(struct timer_list *timer) { BUG_ON(timer_pending(timer)); __mod_timer(timer, timer->expires); } |
?
3、刪除定時(shí)器:
int del_timer(struct timer_list *timer);
從內(nèi)核中刪除已經(jīng)注冊的定時(shí)器timer。如果該定時(shí)器是活動(dòng)的,則返回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、修改定時(shí)器的定時(shí)時(shí)間:
?
| 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); } |
?
從代碼可以看出,如果所給的要修改的時(shí)間等于定時(shí)器原來的時(shí)間并且定時(shí)器現(xiàn)在正處于活動(dòng)狀態(tài),則不修改,返回1,否則修改定時(shí)器時(shí)間,返回0。mod_timer()是一個(gè)非有效的更新處于活動(dòng)狀態(tài)的定時(shí)器的時(shí)間的方法,如果定時(shí)器處于非活動(dòng)狀態(tài),則會(huì)激活定時(shí)器。在功能上,mod_timer()等價(jià)于:
del_timer(timer);
timer->expires=expires;
add_timer(timer);
?
五、內(nèi)核延時(shí)函數(shù):
1、短延時(shí):
ndelay(unsigned long nsecs); /*延時(shí)nsecs納秒*/
udelay(unsigned long usecs); /*延時(shí)usecs微秒*/
mdelay(unsigned long msecs); /*延時(shí)msecs毫秒*/
此三個(gè)宏延時(shí)的本質(zhì)是“忙等待”,也就是說在延時(shí)的過程中,并沒有放棄CPU,而是根據(jù)CPU的頻率進(jìn)行一定次數(shù)的循環(huán)來達(dá)到延時(shí)的目的。三個(gè)宏最終都是將各自的參數(shù)(延時(shí)的時(shí)間)經(jīng)過一定的換算調(diào)用delay_loop()函數(shù)來循環(huán)耗時(shí)達(dá)到延時(shí),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,則結(jié)束,否則跳到標(biāo)號2處,形成循環(huán)。這就是所謂的“忙等待”。
2、長延時(shí):
在內(nèi)核中,一個(gè)直觀的延時(shí)的方法是將所要延遲的時(shí)間設(shè)置的當(dāng)前的jiffies加上要延遲的時(shí)間,這樣就可以簡單的通過比較當(dāng)前的jiffies和設(shè)置的時(shí)間來判斷延時(shí)的時(shí)間時(shí)候到來。針對此方法,內(nèi)核中提供了簡單的宏用于判斷延時(shí)是否完成。
time_after(jiffies,delay); /*此刻如果還沒有到達(dá)延時(shí)的時(shí)間,則返回真,否則返回0*/
time_before(jiffies,delay);/*如果延時(shí)還沒有完成,則返回真,否則返回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循環(huán)的判斷語句,進(jìn)行忙等待延時(shí)。
3、睡眠延時(shí):
與忙等待延時(shí)相對的是睡眠延時(shí),在延時(shí)的過程中,進(jìn)程是處于睡眠狀態(tài),這意味著其他的任務(wù)可以在這是被調(diào)度執(zhí)行,提高了CPU的有效利用率。在睡眠給定的時(shí)間后,任務(wù)又被重新調(diào)度執(zhí)行。內(nèi)核提供的睡眠延時(shí)函數(shù)是:
void msleep(unsigned int msecs);
unsigned long msleep_interruptible(unsigned int msecs);
則會(huì)兩個(gè)函數(shù)的區(qū)別是調(diào)用msleep()函數(shù)進(jìn)行睡眠延時(shí)的進(jìn)程不能被信號打斷,而調(diào)用msleep_interruptible()函數(shù)延時(shí)的進(jìn)程可以被信號喚醒。一下給出msleep()函數(shù)的代碼:
?
| void msleep(unsigned int msecs) { unsigned long timeout = msecs_to_jiffies(msecs) + 1; ? while (timeout) timeout = schedule_timeout_uninterruptible(timeout); } |
可以看出msleep()本質(zhì)還是依靠schedule_timeout_uninterruptible()函數(shù)實(shí)現(xiàn)的。在每次被重新調(diào)度執(zhí)行時(shí),如果睡眠沒有完成,則重新進(jìn)入睡眠直到到達(dá)睡眠的時(shí)間。
轉(zhuǎn)自:http://blog.csdn.net/edison0716/article/details/5415364
轉(zhuǎn)載于:https://www.cnblogs.com/wangfengju/archive/2013/04/13/6173185.html
總結(jié)
以上是生活随笔為你收集整理的Linux 2.6 内核定时器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CodeDom 笔记整理
- 下一篇: 最大字段和各种不同算法实现(参考编程珠玑