版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、Linux定時(shí)器的使用內(nèi)核定時(shí)器是內(nèi)核用來控制在未來某個(gè)時(shí)間點(diǎn)(基于jiffies)調(diào)度執(zhí)行某個(gè)函數(shù)的一種機(jī)制,其實(shí)現(xiàn)位于 <linux/timer.h> 和 kernel/timer.c 文件中。被調(diào)度的函數(shù)肯定是異步執(zhí)行的,它類似于一種“軟件中斷”,而且是處于非進(jìn)程的上下文中,所以調(diào)度函數(shù)必須遵守以下規(guī)則:1) 沒有 current 指針、不允許訪問用戶空間。因?yàn)闆]有進(jìn)程上下文,相關(guān)代碼和被中斷的進(jìn)程沒有任何聯(lián)系。2) 不能執(zhí)行休眠(或可能引起休眠的函數(shù))和調(diào)度。3) 任何被訪問的數(shù)據(jù)結(jié)構(gòu)都應(yīng)該針對并發(fā)訪問進(jìn)行保護(hù),以防止競爭條件。內(nèi)核定時(shí)器的調(diào)度函數(shù)運(yùn)行過一次后就不會(huì)再被運(yùn)行
2、了(相當(dāng)于自動(dòng)注銷),但可以通過在被調(diào)度的函數(shù)中重新調(diào)度自己來周期運(yùn)行。在SMP系統(tǒng)中,調(diào)度函數(shù)總是在注冊它的同一CPU上運(yùn)行,以盡可能獲得緩存的局域性。內(nèi)核定時(shí)器的數(shù)據(jù)結(jié)構(gòu)struct timer_list struct list_head entry; unsigned long expires; void (*function)(unsigned long); unsigned long data; struct tvec_base *base; /* . */;其中 expires 字段表示期望定時(shí)器執(zhí)行的 jiffies 值,到達(dá)該 jiffies 值時(shí),將調(diào)用 function 函
3、數(shù),并傳遞 data 作為參數(shù)。當(dāng)一個(gè)定時(shí)器被注冊到內(nèi)核之后,entry 字段用來連接該定時(shí)器到一個(gè)內(nèi)核鏈表中。base 字段是內(nèi)核內(nèi)部實(shí)現(xiàn)所用的。需要注意的是 expires 的值是32位的,因?yàn)閮?nèi)核定時(shí)器并不適用于長的未來時(shí)間點(diǎn)。初始化在使用 struct timer_list 之前,需要初始化該數(shù)據(jù)結(jié)構(gòu),確保所有的字段都被正確地設(shè)置。初始化有兩種方法。方法一:DEFINE_TIMER(timer_name, function_name, expires_value, data);該宏會(huì)定義一個(gè)名叫 timer_name 內(nèi)核定時(shí)器,并初始化其 function, expires, nam
4、e 和 base 字段。方法二:struct timer_list mytimer;setup_timer(&mytimer, (*function)(unsigned long), unsigned long data);mytimer.expires = jiffies + 5*HZ;注意,無論用哪種方法初始化,其本質(zhì)都只是給字段賦值,所以只要在運(yùn)行 add_timer() 之前,expires, function 和 data 字段都可以直接再修改。關(guān)于上面這些宏和函數(shù)的定義,參見 include/linux/timer.h。注冊定時(shí)器要生效,還必須被連接到內(nèi)核專門的鏈表中,這可
5、以通過 add_timer(struct timer_list *timer) 來實(shí)現(xiàn)。重新注冊要修改一個(gè)定時(shí)器的調(diào)度時(shí)間,可以通過調(diào)用 mod_timer(struct timer_list *timer, unsigned long expires)。mod_timer() 會(huì)重新注冊定時(shí)器到內(nèi)核,而不管定時(shí)器函數(shù)是否被運(yùn)行過。注銷注銷一個(gè)定時(shí)器,可以通過 del_timer(struct timer_list *timer) 或 del_timer_sync(struct timer_list *timer)。其中 del_timer_sync 是用在 SMP 系統(tǒng)上的(在非SMP系統(tǒng)上
6、,它等于del_timer),當(dāng)要被注銷的定時(shí)器函數(shù)正在另一個(gè) cpu 上運(yùn)行時(shí),del_timer_sync() 會(huì)等待其運(yùn)行完,所以這個(gè)函數(shù)會(huì)休眠。另外還應(yīng)避免它和被調(diào)度的函數(shù)爭用同一個(gè)鎖。對于一個(gè)已經(jīng)被運(yùn)行過且沒有重新注冊自己的定時(shí)器而言,注銷函數(shù)其實(shí)也沒什么事可做。int timer_pending(const struct timer_list *timer)這個(gè)函數(shù)用來判斷一個(gè)定時(shí)器是否被添加到了內(nèi)核鏈表中以等待被調(diào)度運(yùn)行。注意,當(dāng)一個(gè)定時(shí)器函數(shù)即將要被運(yùn)行前,內(nèi)核會(huì)把相應(yīng)的定時(shí)器從內(nèi)核鏈表中刪除(相當(dāng)于注銷)一個(gè)簡單的例子#include <linux/module.h&g
7、t;#include <linux/timer.h>#include <linux/jiffies.h>struct timer_list mytimer;static void myfunc(unsigned long data) printk("%sn", (char *)data); mod_timer(&mytimer, jiffies + 2*HZ);static int _init mytimer_init(void) setup_timer(&mytimer, myfunc, (unsigned long)"H
8、ello, world!"); mytimer.expires = jiffies + HZ; add_timer(&mytimer); return 0;static void _exit mytimer_exit(void) del_timer(&mytimer);module_init(mytimer_init);module_exit(mytimer_exit); *-761 Linux內(nèi)核對定時(shí)器的描述 Linux內(nèi)核2.4版中去掉了老版本內(nèi)核中的靜態(tài)定時(shí)器機(jī)制,而只留下動(dòng)態(tài)定時(shí)器。相應(yīng)地在timer_bh()函數(shù)中也不再通過run_ol
9、d_timers()函數(shù)來運(yùn)行老式的靜態(tài)定時(shí)器。動(dòng)態(tài)定時(shí)器與靜態(tài)定時(shí)器這二個(gè)概念是相對于Linux內(nèi)核定時(shí)器機(jī)制的可擴(kuò)展功能而言的,動(dòng)態(tài)定時(shí)器是指內(nèi)核的定時(shí)器隊(duì)列是可以動(dòng)態(tài)變化的,然而就定時(shí)器本身而言,二者并無本質(zhì)的區(qū)別??紤]到靜態(tài)定時(shí)器機(jī)制的能力有限,因此Linux內(nèi)核2.4版中完全去掉了以前的靜態(tài)定時(shí)器機(jī)制。 timer_create(2): 創(chuàng)建了一個(gè)定時(shí)器。timer_settime(2): 啟動(dòng)或者停止一個(gè)定時(shí)器。timer_gettime(2): 返回到下一次到期的剩余時(shí)間值和定時(shí)器定義的時(shí)間間隔。出現(xiàn)該接口的原因是,如果用戶定義了一個(gè) 1ms 的定時(shí)器,可能當(dāng)時(shí)系統(tǒng)負(fù)荷
10、很重,導(dǎo)致該定時(shí)器實(shí)際山 10ms 后才超時(shí),這種情況下,overrun=9ms 。timer_getoverrun(2): 返回上次定時(shí)器到期時(shí)超限值。timer_delete(2): 停止并刪除一個(gè)定時(shí)器。上面最重要的接口是 timer_create(2),其中,clockid 表明了要使用的時(shí)鐘類型,在 POSIX 中要求必須實(shí)現(xiàn) CLOCK_REALTIME 類型的時(shí)鐘。 evp 參數(shù)指明了在定時(shí)到期后,調(diào)用者被通知的方式。該結(jié)構(gòu)體定義如下 :Linux在include/linux/timer.h頭文件中定義了數(shù)據(jù)結(jié)構(gòu)timer_list來描述一個(gè)內(nèi)核定時(shí)器: struct&
11、#160;timer_list struct list_head list; unsigned long expires; unsigned long data; void (*function)(unsigned long); 各數(shù)據(jù)成員的含義如下: (1)雙向鏈表元素list:用來將多個(gè)定時(shí)器連接成一條雙向循環(huán)隊(duì)列。 (2)expires:指定定時(shí)器到期的時(shí)間,這個(gè)時(shí)間被表示成自系統(tǒng)啟動(dòng)以來的時(shí)鐘滴答計(jì)數(shù)(也即
12、時(shí)鐘節(jié)拍數(shù))。當(dāng)一個(gè)定時(shí)器的expires值小于或等于jiffies變量時(shí),我們就說這個(gè)定時(shí)器已經(jīng)超時(shí)或到期了。在初始化一個(gè)定時(shí)器后,通常把它的expires域設(shè)置成當(dāng)前expires變量的當(dāng)前值加上某個(gè)時(shí)間間隔值(以時(shí)鐘滴答次數(shù)計(jì))。 (3)函數(shù)指針function:指向一個(gè)可執(zhí)行函數(shù)。當(dāng)定時(shí)器到期時(shí),內(nèi)核就執(zhí)行function所指定的函數(shù)。而data域則被內(nèi)核用作function函數(shù)的調(diào)用參數(shù)。 內(nèi)核函數(shù)init_timer()用來初始化一個(gè)定時(shí)器。實(shí)際上,這個(gè)初始化函數(shù)僅僅將結(jié)構(gòu)中的list成員初始化為空。如下所示(include/linux/timer.h):
13、60;static inline void init_timer(struct timer_list * timer) timer->list.next = timer->list.prev = NULL; 由于定時(shí)器通常被連接在一個(gè)雙向循環(huán)隊(duì)列中等待執(zhí)行(此時(shí)我們說定時(shí)器處于pending狀態(tài))。因此函數(shù)time_pending()就可以用list成員是否為空來判斷一個(gè)定時(shí)器是否處于pending狀態(tài)。如下所示 (includ
14、e/linux/timer.h): static inline int timer_pending (const struct timer_list * timer) return timer->list.next != NULL; 時(shí)間比較操作 在定時(shí)器應(yīng)用中經(jīng)常需要比較兩個(gè)時(shí)間值,以確定timer是否超時(shí),所以Linux內(nèi)核在timer.h頭文件中定義了4個(gè)時(shí)間關(guān)系比較操作宏。這里我們說時(shí)刻a在時(shí)刻b之后,就意
15、味著時(shí)間值ab。Linux強(qiáng)烈推薦用戶使用它所定義的下列4個(gè)時(shí)間比較操作宏(include/linux/timer.h): #define time_after(a,b) (long)(b) - (long)(a) < 0) #define time_before(a,b) time_after(b,a) #define time_after_eq(a,b) (long)(a) - (long)(b) >= 0)&
16、#160;#define time_before_eq(a,b) time_after_eq(b,a) 762 動(dòng)態(tài)內(nèi)核定時(shí)器機(jī)制的原理 Linux是怎樣為其內(nèi)核定時(shí)器機(jī)制提供動(dòng)態(tài)擴(kuò)展能力的呢?其關(guān)鍵就在于“定時(shí)器向量”的概念。所謂“定時(shí)器向量”就是指這樣一條雙向循環(huán)定時(shí)器隊(duì)列(對列中的每一個(gè)元素都是一個(gè)timer_list結(jié)構(gòu)):對列中的所有定時(shí)器都在同一個(gè)時(shí)刻到期,也即對列中的每一個(gè)timer_list結(jié)構(gòu)都具有相同的expires值。顯然,可以用一個(gè)timer_list結(jié)構(gòu)類型的指針來表示一個(gè)定時(shí)器向量。 顯然,定時(shí)器expir
17、es成員的值與jiffies變量的差值決定了一個(gè)定時(shí)器將在多長時(shí)間后到期。在32位系統(tǒng)中,這個(gè)時(shí)間差值的最大值應(yīng)該是0xffffffff。因此如果是基于“定時(shí)器向量”基本定義,內(nèi)核將至少要維護(hù)0xffffffff個(gè)timer_list結(jié)構(gòu)類型的指針,這顯然是不現(xiàn)實(shí)的。 另一方面,從內(nèi)核本身這個(gè)角度看,它所關(guān)心的定時(shí)器顯然不是那些已經(jīng)過期而被執(zhí)行過的定時(shí)器(這些定時(shí)器完全可以被丟棄),也不是那些要經(jīng)過很長時(shí)間才會(huì)到期的定時(shí)器,而是那些當(dāng)前已經(jīng)到期或者馬上就要到期的定時(shí)器(注意!時(shí)間間隔是以滴答次數(shù)為計(jì)數(shù)單位的)。 基于上述考慮,并假定一個(gè)定時(shí)器要經(jīng)過interval個(gè)時(shí)鐘滴答
18、后才到期(intervalexpiresjiffies),則Linux采用了下列思想來實(shí)現(xiàn)其動(dòng)態(tài)內(nèi)核定時(shí)器機(jī)制:對于那些0interval255的定時(shí)器,Linux嚴(yán)格按照定時(shí)器向量的基本語義來組織這些定時(shí)器,也即Linux內(nèi)核最關(guān)心那些在接下來的255個(gè)時(shí)鐘節(jié)拍內(nèi)就要到期的定時(shí)器,因此將它們按照各自不同的expires值組織成256個(gè)定時(shí)器向量。而對于那些256interval0xffffffff的定時(shí)器,由于他們離到期還有一段時(shí)間,因此內(nèi)核并不關(guān)心他們,而是將它們以一種擴(kuò)展的定時(shí)器向量語義(或稱為“松散的定時(shí)器向量語義”)進(jìn)行組織。所謂“松散的定時(shí)器向量語義”就是指:各定時(shí)器的expire
19、s值可以互不相同的一個(gè)定時(shí)器隊(duì)列。 具體的組織方案可以分為兩大部分: (1)對于內(nèi)核最關(guān)心的、interval值在0,255之間的前256個(gè)定時(shí)器向量,內(nèi)核是這樣組織它們的:這256個(gè)定時(shí)器向量被組織在一起組成一個(gè)定時(shí)器向量數(shù)組,并作為數(shù)據(jù)結(jié)構(gòu)timer_vec_root的一部分,該數(shù)據(jù)結(jié)構(gòu)定義在kernel/timer.c文件中,如下述代碼段所示: /* * Event timer code */ #define TVN_BITS 6 #define TVR_BI
20、TS 8 #define TVN_SIZE (1 << TVN_BITS) #define TVR_SIZE (1 << TVR_BITS) #define TVN_MASK (TVN_SIZE - 1) #define TVR_MASK (TVR_SIZE - 1) struct timer_vec int i
21、ndex; struct list_head vecTVN_SIZE; struct timer_vec_root int index; struct list_head vecTVR_SIZE; static struct timer_vec tv5; static struct timer_vec tv4; static struct timer_
22、vec tv3; static struct timer_vec tv2; static struct timer_vec_root tv1; static struct timer_vec * const tvecs = (struct timer_vec *)&tv1, &tv2, &tv3, &tv4, &tv
23、5 #define NOOF_TVECS (sizeof(tvecs) / sizeof(tvecs0) 基于數(shù)據(jù)結(jié)構(gòu)timer_vec_root,Linux定義了一個(gè)全局變量tv1,以表示內(nèi)核所關(guān)心的前256個(gè)定時(shí)器向量。這樣內(nèi)核在處理是否有到期定時(shí)器時(shí),它就只從定時(shí)器向量數(shù)組tv1.vec256中的某個(gè)定時(shí)器向量內(nèi)進(jìn)行掃描。而tv1的index字段則指定當(dāng)前正在掃描定時(shí)器向量數(shù)組tv1.vec256中的哪一個(gè)定時(shí)器向量,也即該數(shù)組的索引,其初值為0,最大值為255(以256為模)。每個(gè)時(shí)鐘節(jié)拍時(shí)index字段都會(huì)加1。
24、顯然,index字段所指定的定時(shí)器向量tv1.vecindex中包含了當(dāng)前時(shí)鐘節(jié)拍內(nèi)已經(jīng)到期的所有動(dòng)態(tài)定時(shí)器。而定時(shí)器向量tv1.vecindexk則包含了接下來第k個(gè)時(shí)鐘節(jié)拍時(shí)刻將到期的所有動(dòng)態(tài)定時(shí)器。當(dāng)index值又重新變?yōu)?時(shí),就意味著內(nèi)核已經(jīng)掃描了tv1變量中的所有256個(gè)定時(shí)器向量。在這種情況下就必須將那些以松散定時(shí)器向量語義來組織的定時(shí)器向量補(bǔ)充到tv1中來。 (2)而對于內(nèi)核不關(guān)心的、interval值在0xff,0xffffffff之間的定時(shí)器,它們的到期緊迫程度也隨其interval值的不同而不同。顯然interval值越小,定時(shí)器緊迫程度也越高。因此在將它們以松散
25、定時(shí)器向量進(jìn)行組織時(shí)也應(yīng)該區(qū)別對待。通常,定時(shí)器的interval值越小,它所處的定時(shí)器向量的松散度也就越低(也即向量中的各定時(shí)器的expires值相差越?。欢鴌nterval值越大,它所處的定時(shí)器向量的松散度也就越大(也即向量中的各定時(shí)器的expires值相差越大)。 內(nèi)核規(guī)定,對于那些滿足條件:0x100interval0x3fff的定時(shí)器,只要表達(dá)式(interval>>8)具有相同值的定時(shí)器都將被組織在同一個(gè)松散定時(shí)器向量中。因此,為組織所有滿足條件0x100interval0x3fff的定時(shí)器,就需要2664個(gè)松散定時(shí)器向量。同樣地,為方便起見,這64個(gè)松散定
26、時(shí)器向量也放在一起形成數(shù)組,并作為數(shù)據(jù)結(jié)構(gòu)timer_vec的一部分?;跀?shù)據(jù)結(jié)構(gòu)timer_vec,Linux定義了全局變量tv2,來表示這64條松散定時(shí)器向量。如上述代碼段所示。 對于那些滿足條件0x4000interval0xfffff的定時(shí)器,只要表達(dá)式(interval>>86)的值相同的定時(shí)器都將被放在同一個(gè)松散定時(shí)器向量中。同樣,要組織所有滿足條件0x4000interval0xfffff的定時(shí)器,也需要2664個(gè)松散定時(shí)器向量。類似地,這64個(gè)松散定時(shí)器向量也可以用一個(gè)timer_vec結(jié)構(gòu)來描述,相應(yīng)地Linux定義了tv3全局變量來表示這64個(gè)松散定時(shí)
27、器向量。 對于那些滿足條件0x100000interval0x3ffffff的定時(shí)器,只要表達(dá)式(interval>>866)的值相同的定時(shí)器都將被放在同一個(gè)松散定時(shí)器向量中。同樣,要組織所有滿足條件0x100000interval0x3ffffff的定時(shí)器,也需要2664個(gè)松散定時(shí)器向量。類似地,這64個(gè)松散定時(shí)器向量也可以用一個(gè)timer_vec結(jié)構(gòu)來描述,相應(yīng)地Linux定義了tv4全局變量來表示這64個(gè)松散定時(shí)器向量。 對于那些滿足條件0x4000000interval0xffffffff的定時(shí)器,只要表達(dá)式(interval>>8666)的
28、值相同的定時(shí)器都將被放在同一個(gè)松散定時(shí)器向量中。同樣,要組織所有滿足條件0x4000000interval0xffffffff的定時(shí)器,也需要2664個(gè)松散定時(shí)器向量。類似地,這64個(gè)松散定時(shí)器向量也可以用一個(gè)timer_vec結(jié)構(gòu)來描述,相應(yīng)地Linux定義了tv5全局變量來表示這64個(gè)松散定時(shí)器向量。 最后,為了引用方便,Linux定義了一個(gè)指針數(shù)組tvecs,來分別指向tv1、tv2、tv5結(jié)構(gòu)變量。如上述代碼所示。 763 內(nèi)核動(dòng)態(tài)定時(shí)器機(jī)制的實(shí)現(xiàn) 在內(nèi)核動(dòng)態(tài)定時(shí)器機(jī)制的實(shí)現(xiàn)中,有三個(gè)操作時(shí)非常重要的:(1)將一個(gè)定時(shí)器插入到它應(yīng)該所處的定時(shí)器向
29、量中。(2)定時(shí)器的遷移,也即將一個(gè)定時(shí)器從它原來所處的定時(shí)器向量遷移到另一個(gè)定時(shí)器向量中。(3)掃描并執(zhí)行當(dāng)前已經(jīng)到期的定時(shí)器。 7631 動(dòng)態(tài)定時(shí)器機(jī)制的初始化 函數(shù)init_timervecs()實(shí)現(xiàn)對動(dòng)態(tài)定時(shí)器機(jī)制的初始化。該函數(shù)僅被sched_init()初始化例程所調(diào)用。動(dòng)態(tài)定時(shí)器機(jī)制初始化過程的主要任務(wù)就是將tv1、tv2、tv5這5個(gè)結(jié)構(gòu)變量中的定時(shí)器向量指針數(shù)組vec初始化為NULL。如下所示(kernel/timer.c): void init_timervecs (void) int
30、0;i; for (i = 0; i < TVN_SIZE; i+) INIT_LIST_HEAD(tv5.vec + i); INIT_LIST_HEAD(tv4.vec + i); INIT_LIST_HEAD(tv3.vec + i); INIT_LIST_HEAD(tv2.vec + i); for (i = 0
31、; i < TVR_SIZE; i+) INIT_LIST_HEAD(tv1.vec + i); 上述函數(shù)中的宏TVN_SIZE是指timer_vec結(jié)構(gòu)類型中的定時(shí)器向量指針數(shù)組vec的大小,值為64。宏TVR_SIZE是指timer_vec_root結(jié)構(gòu)類型中的定時(shí)器向量數(shù)組vec的大小,值為256。 7632 動(dòng)態(tài)定時(shí)器的時(shí)鐘滴答基準(zhǔn)timer_jiffies 由于動(dòng)態(tài)定時(shí)器是在時(shí)鐘中斷的Bottom Half中被執(zhí)行的,而從TIMER_BH向量被激
32、活到其timer_bh()函數(shù)真正執(zhí)行這段時(shí)間內(nèi)可能會(huì)有幾次時(shí)鐘中斷發(fā)生。因此內(nèi)核必須記住上一次運(yùn)行定時(shí)器機(jī)制是什么時(shí)候,也即內(nèi)核必須保存上一次運(yùn)行定時(shí)器機(jī)制時(shí)的jiffies值。為此,Linux在kernel/timer.c文件中定義了全局變量timer_jiffies來表示上一次運(yùn)行定時(shí)器機(jī)制時(shí)的jiffies值。該變量的定義如下所示: static unsigned long timer_jiffies; 7633 對內(nèi)核動(dòng)態(tài)定時(shí)器鏈表的保護(hù) 由于內(nèi)核動(dòng)態(tài)定時(shí)器鏈表是一種系統(tǒng)全局共享資源,為了實(shí)現(xiàn)對它的互斥訪問,Lin
33、ux定義了專門的自旋鎖timerlist_lock來保護(hù)。任何想要訪問動(dòng)態(tài)定時(shí)器鏈表的代碼段都首先必須先持有該自旋鎖,并且在訪問結(jié)束后釋放該自旋鎖。其定義如下(kernel/timer.c): /* Initialize both explicitly - let's try to have them in the same cache line */ spinlock_t timerlist_lock
34、60;= SPIN_LOCK_UNLOCKED;7634將一個(gè)定時(shí)器插入到鏈表中函數(shù)add_timer()用來將參數(shù)timer指針?biāo)赶虻亩〞r(shí)器插入到一個(gè)合適的定時(shí)器鏈表中。它首先調(diào)用timer_pending()函數(shù)判斷所指定的定時(shí)器是否已經(jīng)位于在某個(gè)定時(shí)器向量中等待執(zhí)行。如果是,則不進(jìn)行任何操作,只是打印一條內(nèi)核告警信息就返回了;如果不是,則調(diào)用internal_add_timer()函數(shù)完成實(shí)際的插入操作。其源碼如下(kernel/timer.c): Void add_timer(structtimer_list*timer) unsignedlongflags;spin_lo
35、ck_irqsave(&timerlist_lock,flags);if(timer_pending(timer)gotobug;internal_add_timer(timer);spin_unlock_irqrestore(&timerlist_lock,flags);return;bug:spin_unlock_irqrestore(&timerlist_lock,flags);printk("bug:kerneltimeraddedtwiceat%p.n",_builtin_return_address(0); 函數(shù)internal_add_
36、timer()用于將一個(gè)不處于任何定時(shí)器向量中的定時(shí)器插入到它應(yīng)該所處的定時(shí)器向量中去(根據(jù)定時(shí)器的expires值來決定)。如下所示(kernel/timer.c): staticinlinevoidinternal_add_timer(structtimer_list*timer)/*mustbecli-edwhencallingthis*/unsignedlongexpires=timer->expires;unsignedlongidx=expires-timer_jiffies;structlist_head*vec;if(idx<TVR_SIZE)inti=expire
37、s&TVR_MASK;vec=tv1.vec i;elseif(idx<1<<(TVR_BITS TVN_BITS)inti=(expires>>TVR_BITS)&TVN_MASK;vec=tv2.vec i;elseif(idx<1<<(TVR_BITS 2*TVN_BITS)inti=(expires>>(TVR_BITS TVN_BITS)&TVN_MASK;vec=tv3.vec i;elseif(idx<1<<(TVR_BITS 3*TVN_BITS)inti=(expires&g
38、t;>(TVR_BITS 2*TVN_BITS)&TVN_MASK;vec=tv4.vec i;elseif(signedlong)idx<0)/*canhappenifyouaddatimerwithexpires=jiffies,*oryousetatimertogooffinthepast*/vec=tv1.vec tv1.index;elseif(idx<=0xffffffffUL)inti=(expires>>(TVR_BITS 3*TVN_BITS)&TVN_MASK;vec=tv5.vec i;else/*Canonlygethere
39、onarchitectureswith64-bitjiffies*/INIT_LIST_HEAD(&timer->list);return;/*TimersareFIFO!*/list_add(&timer->list,vec->prev); 對該函數(shù)的注釋如下:(1)首先,計(jì)算定時(shí)器的expires值與timer_jiffies的插值(注意!這里應(yīng)該使用動(dòng)態(tài)定時(shí)器自己的時(shí)間基準(zhǔn)),這個(gè)差值就表示這個(gè)定時(shí)器相對于上一次運(yùn)行定時(shí)器機(jī)制的那個(gè)時(shí)刻還需要多長時(shí)間間隔才到期。局部變量idx保存這個(gè)差值。(2)根據(jù)idx的值確定這個(gè)定時(shí)器應(yīng)被插入到哪一個(gè)定時(shí)器向量中。其
40、具體的確定方法我們在節(jié)已經(jīng)說過了,這里不再詳述。最后,定時(shí)器向量的頭部指針vec表示這個(gè)定時(shí)器應(yīng)該所處的定時(shí)器向量鏈表頭部。(3)最后,調(diào)用list_add()函數(shù)將定時(shí)器插入到vec指針?biāo)赶虻亩〞r(shí)器隊(duì)列的尾部。7635修改一個(gè)定時(shí)器的expires值當(dāng)一個(gè)定時(shí)器已經(jīng)被插入到內(nèi)核動(dòng)態(tài)定時(shí)器鏈表中后,我們還可以修改該定時(shí)器的expires值。函數(shù)mod_timer()實(shí)現(xiàn)這一點(diǎn)。如下所示(kernel/timer.c): intmod_timer(structtimer_list*timer,unsignedlongexpires)intret;unsignedlongflags;spin_l
41、ock該函數(shù)首先根據(jù)參數(shù)expires值更新定時(shí)器的expires成員。然后調(diào)用detach_timer()函數(shù)將該定時(shí)器從它原來所屬的鏈表中刪除。最后調(diào)用internal_add_timer()函數(shù)將該定時(shí)器根據(jù)它新的expires值重新插入到相應(yīng)的鏈表中。函數(shù)detach_timer()首先調(diào)用timer_pending()來判斷指定的定時(shí)器是否已經(jīng)處于某個(gè)鏈表中,如果定時(shí)器原來就不處于任何鏈表中,則detach_timer()函數(shù)什么也不做,直接返回0值,表示失敗。否則,就調(diào)用list_del()函數(shù)將定時(shí)器從它原來所處的鏈表中摘除。如下所示(kernel/timer.c): stati
42、cinlineintdetach_timer(structtimer_list*timer)if(!timer_pending(timer)return0;list_del(&timer->list);return1; 7636刪除一個(gè)定時(shí)器函數(shù)del_timer()用來將一個(gè)定時(shí)器從相應(yīng)的內(nèi)核定時(shí)器隊(duì)列中刪除。該函數(shù)實(shí)際上是對detach_timer()函數(shù)的高層封裝。如下所示(kernel/timer.c): intdel_timer(structtimer_list*timer)intret;unsignedlongflags;spin_lock_irqsave(&
43、timerlist_lock,flags);ret=detach_timer(timer);timer->list.next=timer->list.prev=NULL;spin_unlock_irqrestore(&timerlist_lock,flags);returnret; 軟件開發(fā)網(wǎng) 7637定時(shí)器遷移操作由于一個(gè)定時(shí)器的interval值會(huì)隨著時(shí)間的不斷流逝(即jiffies值的不斷增大)而不斷變小,因此那些原本到期緊迫程度較低的定時(shí)器會(huì)隨著jiffies值的不斷增大而成為既將馬上到期的定時(shí)器。比如定時(shí)器向量tv2.vec0中的定時(shí)器在經(jīng)過256個(gè)時(shí)鐘滴答后會(huì)成
44、為未來256個(gè)時(shí)鐘滴答內(nèi)會(huì)到期的定時(shí)器。因此,定時(shí)器在內(nèi)核動(dòng)態(tài)定時(shí)器鏈表中的位置也應(yīng)相應(yīng)地隨著改變。改變的規(guī)則是:當(dāng)tv1.index重新變?yōu)?時(shí)(意味著tv1中的256個(gè)定時(shí)器向量都已被內(nèi)核掃描一遍了,從而使tv1中的256個(gè)定時(shí)器向量變?yōu)榭眨?,則用tv2.vecindex定時(shí)器向量中的定時(shí)器去填充tv1,同時(shí)使tv2.index加1(它以64為模)。當(dāng)tv2.index重新變?yōu)?(意味著tv2中的64個(gè)定時(shí)器向量都已經(jīng)被全部填充到tv1中去了,從而使得tv2變?yōu)榭眨?,則用tv3.vecindex定時(shí)器向量中的定時(shí)器去填充tv2。如此一直類推下去,直到tv5。函數(shù)cascade_timers
45、()完成這種定時(shí)器遷移操作,該函數(shù)只有一個(gè)timer_vec結(jié)構(gòu)類型指針的參數(shù)tv。這個(gè)函數(shù)將把定時(shí)器向量tv->vectv->index中的所有定時(shí)器重新填充到上一層定時(shí)器向量中去。如下所示(kernel/timer.c): staticinlinevoidcascade_timers(structtimer_vec*tv)/*cascadeallthetimersfromtvuponelevel*/structlist_head*head,*curr,*next;head=tv->vec tv->index;curr=head->next;/*Wearerem
46、oving_all_timersfromthelist,sowedon'thaveto*detachthemindividually,justclearthelistafterwards.*/while(curr!=head)structtimer_list*tmp;tmp=list_entry(curr,structtimer_list,list);next=curr->next;list_del(curr);/notneededinternal_add_timer(tmp);curr=next;INIT_LIST_HEAD(head);tv->index=(tv->
47、;index 1)&TVN_MASK; 對該函數(shù)的注釋如下:(1)首先,用指針head指向定時(shí)器頭部向量頭部的list_head結(jié)構(gòu)。指針curr指向定時(shí)器向量中的第一個(gè)定時(shí)器。(2)然后,用一個(gè)while循環(huán)來遍歷定時(shí)器向量tv->vectv->index。由于定時(shí)器向量是一個(gè)雙向循環(huán)隊(duì)列,因此循環(huán)的終止條件是curr=head。對于每一個(gè)被掃描的定時(shí)器,循環(huán)體都先調(diào)用list_del()函數(shù)將當(dāng)前定時(shí)器從鏈表中摘除,然后調(diào)用internal_add_timer()函數(shù)重新確定該定時(shí)器應(yīng)該被放到哪個(gè)定時(shí)器向量中去。(3)當(dāng)從while循環(huán)退出后,定時(shí)器向量tv->v
48、ectv->index中所有的定時(shí)器都已被遷移到其它地方(到它們該呆的地方:),因此它本身就成為一個(gè)空隊(duì)列。這里我們顯示地調(diào)用INIT_LIST_HEAD()宏來將定時(shí)器向量的表頭結(jié)構(gòu)初始化為空。(4)最后,將tv->index值加1,當(dāng)然它是以64為模。 7648掃描并執(zhí)行當(dāng)前已經(jīng)到期的定時(shí)器函數(shù)run_timer_list()完成這個(gè)功能。如前所述,該函數(shù)是被timer_bh()函數(shù)所調(diào)用的,因此內(nèi)核定時(shí)器是在時(shí)鐘中斷的BottomHalf中被執(zhí)行的。記住這一點(diǎn)非常重要。全局變量timer_jiffies表示了內(nèi)核上一次執(zhí)行run_timer_list()函數(shù)的時(shí)間,因此jif
49、fies與timer_jiffies的差值就表示了自從上一次處理定時(shí)器以來,期間一共發(fā)生了多少次時(shí)鐘中斷,顯然run_timer_list()函數(shù)必須為期間所發(fā)生的每一次時(shí)鐘中斷補(bǔ)上定時(shí)器服務(wù)。該函數(shù)的源碼如下(kernel/timer.c): staticinlinevoidrun_timer_list(void)spin_lock_irq(&timerlist_lock);while(long)(jiffies-timer_jiffies)>=0)structlist_head*head,*curr;if(!tv1.index)intn=1;docascade_timers(
50、tvecsn);while(tvecsn->index=1&& n<NOOF_TVECS);repeat:head=tv1.vec tv1.index;curr=head->next;if(curr!=head)structtimer_list*timer;void(*fn)(unsignedlong);unsignedlongdata;timer=list_entry(curr,structtimer_list,list);fn=timer->function;data=timer->data;detach_timer(timer);timer-
51、>list.next=timer->list.prev=NULL;timer_enter(timer);spin_unlock_irq(&timerlist_lock);fn(data);spin_lock_irq(&timerlist_lock);timer_exit();gotorepeat; timer_jiffies;tv1.index=(tv1.index 1)&TVR_MASK;spin_unlock_irq(&timerlist_lock); 函數(shù)run_timer_list()的執(zhí)行過程主要就是用一個(gè)大while循環(huán)來為時(shí)鐘中斷執(zhí)行定
52、時(shí)器服務(wù),每一次循環(huán)服務(wù)一次時(shí)鐘中斷。因此一共要執(zhí)行(jiffiestimer_jiffies1)次循環(huán)。循環(huán)體所執(zhí)行的服務(wù)步驟如下:(1)首先,判斷tv1.index是否為0,如果為0則需要從tv2中補(bǔ)充定時(shí)器到tv1中來。但tv2也可能為空而需要從tv3中補(bǔ)充定時(shí)器,因此用一個(gè)dowhile循環(huán)來調(diào)用cascade_timer()函數(shù)來依次視需要從tv2中補(bǔ)充tv1,從tv3中補(bǔ)充tv2、從tv5中補(bǔ)充tv4。顯然如果tvi.index=0(2i5),則對于tvi執(zhí)行cascade_timers()函數(shù)后,tvi.index肯定為1。反過來講,如果對tvi執(zhí)行過cascade_timers
53、()函數(shù)后tvi.index不等于1,那么可以肯定在未對tvi執(zhí)行cascade_timers()函數(shù)之前,tvi.index值肯定不為0,因此這時(shí)tvi不需要從tv(i 1)中補(bǔ)充定時(shí)器,這時(shí)就可以終止dowhile循環(huán)。(2)接下來,就要執(zhí)行定時(shí)器向量tv1.vectv1.index中的所有到期定時(shí)器。因此這里用一個(gè)gotorepeat循環(huán)從頭到尾依次掃描整個(gè)定時(shí)器對列。由于在執(zhí)行定時(shí)器的關(guān)聯(lián)函數(shù)時(shí)并不需要關(guān)CPU中斷,所以在用detach_timer()函數(shù)將當(dāng)前定時(shí)器從對列中摘除后,就可以調(diào)用spin_unlock_irq()函數(shù)進(jìn)行解鎖和開中斷,然后在執(zhí)行完當(dāng)前定時(shí)器的關(guān)聯(lián)函數(shù)后重新用spin_lock_irq()函數(shù)加鎖和關(guān)中斷。(2)然后,用一個(gè)while循環(huán)來遍歷定時(shí)器向量tv->vectv->index。由于定時(shí)器向量是一個(gè)雙向循環(huán)隊(duì)列,因此循環(huán)的終止條件是curr=head。對于每一個(gè)被掃描的定時(shí)器,循環(huán)體都先調(diào)用list_del()函數(shù)將當(dāng)前定時(shí)器從鏈表中摘除,然后調(diào)用internal_add_timer()函數(shù)重新確定該定時(shí)器應(yīng)該被放到哪個(gè)定時(shí)器向量中去。(3)當(dāng)從while循環(huán)退出后,定時(shí)器向量tv->vectv->index中所有的定時(shí)器都已被遷移到其它地
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 保護(hù)耳朵教案及反思
- 配件風(fēng)險(xiǎn)管理策略
- 服裝行業(yè)招投標(biāo)違規(guī)責(zé)任追究
- 游戲廳裝修施工合同
- 商業(yè)綜合體砌體施工協(xié)議
- 公共安全管理辦法釋義
- 大型電力變電站施工合同
- 勞動(dòng)爭議處理策略研究
- 北京環(huán)保項(xiàng)目采購規(guī)定
- 污水處理工程招投標(biāo)合同
- 小學(xué)數(shù)學(xué)六年級“24點(diǎn)”試題及答案
- Tekla建??旖萱I
- 大航海時(shí)代四特產(chǎn)分布
- 鉆孔灌注樁驗(yàn)孔記錄表
- 法語書信格式(正式版)
- 偏差管理控制程序
- 供應(yīng)商現(xiàn)場審核評分表(詳細(xì))
- 客戶信息登記表
- 二手車復(fù)習(xí)題終極版本
- 畢業(yè)設(shè)計(jì)說明書螺旋精確稱重給料機(jī)設(shè)計(jì)
- 十大危險(xiǎn)作業(yè)
評論
0/150
提交評論