,謝謝!
linux系統定時器,在核心中扮演著重要角色。核心的許多重要實現如任務排程,工作佇列等均以系統定時器關係密切。系統定時器能以可程式設計的頻率中斷處理,這一中斷叫做軟中斷。此頻率即為每秒的定時器節拍數hz。hz的越大,說明定時器節拍越小,執行緒排程的準確性會越高。但hz設得過大,對乙個系統來說並不好,會導cpu開銷過大,反而造成任務排程效率降低。滴答jiffies 變數記錄系統啟動以來,系統定時器已經觸發的次數。也就是說每過一秒jiffies的增量為hz,一般hz=100,hz是可以配置的,在s3c2440 arm linux中配置為200.
下面基於linux2.6.30.4原始碼來**其實現原理及其過程。
要理解系統定時器實現原理,先來看看關係到系統定時器的各種資料結構,其具體的描述引數。
結構體structtimer_list來描述timer的引數,其資料結構如下:
struct timer_list ;
其中:
list_entry結構:
struct list_head ;
tevc_base的結構:
struct tvec_base ____cacheline_aligned;
#define tvn_bits (config_base_small ? 4 :6)
#define tvr_bits (config_base_small ? 6 :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 tvec ;
struct tvec_root ;
可見涉及到系統定時器的資料結構並不多,那麼:對於乙個linux系統中,定時器個數可能會很多,而且每個定時器的超時事件時間並不相同,所以如何管理和處理定時器超時事件,關係到核心效能的高低。它根據不同的定時事件,按時間間分組,將新增的timer定時器建成雙向鍊錶,然後按照一定方式存放於5組tv1~tv5變數中稱為tec_base。對於對稱式多理器(smp)系統還考慮到了timer從乙個cpu遷移到另乙個cpu的情況,相應的tev_base也跟著更改。那它在系統是怎樣實現的呢?現在先從乙個簡單的系統定時器應用例子來看看它的實現過程:
#include #include #include struct timer_list my_timer;
static void my_function(unsigned long data)
static int my_timer_init(void)
static void my_timer_exit(void)
module_init(my_timer_init);
module_exit(my_timer_exit);
module_author( "itspy");
module_license( "gpl");
module_description("linux kernel timerprogramming");
上面例子,實現了乙個定時器事件,將在3 hz(秒)發生。my_imer_init函式中呼叫到的定時器api只有:
init_timer(&my_timer); //用於定時器初始化
add_timer(&my_timer); //增加乙個新的定時器到tev_base向量表中
其中init_timer中呼叫了__init_timer(),這個函式才是真正初始化定時器的:
static void __init_timer(struct timer_list*timer,
const char *name,
struct lock_class_key *key)
新增的定時器初始化,就是完成了乙個timer_list結構初始化過程。
add_timer() --> mod_timer() --> __mod_timer()
其中:
static inline int
__mod_timer(struct timer_list *timer,unsigned long expires, bool pending_only)
else …
new_base= __get_cpu_var(tvec_bases); //獲取本地cpu中的tevc_bases
if(base != new_base)
}timer->expires= expires;
internal_add_timer(base,timer); //分析timer expires及建表過程
out_unlock:
spin_unlock_irqrestore(&base->lock,flags);
return ret;
}
對於新增的timer最後呼叫internal_add_timer(base, timer)加入到相應的timer_list中以完成初始化過程。,這是乙個建表的過程,表的建立情況,關係到表的管理效率。之前我們說到它共有tv1~tv5 組,tv1是乙個很特別的組。每個tv中有各個組員,每個timer是如何新增的呢,看看internal_add_timer()的實現過程:
static void internal_add_timer(structtvec_base *base, struct timer_list *timer)
else if (idx < 1 << (tvr_bits + tvn_bits)) else if (idx < 1 << (tvr_bits + 2 * tvn_bits)) else if (idx < 1 << (tvr_bits + 3 * tvn_bits)) else if ((signed long) idx < 0) else
i= (expires >> (tvr_bits + 3 * tvn_bits)) & tvn_mask;
vec= base->tv5.vec + i;}/*
* timers are fifo:
*/list_add_tail(&timer->entry,vec);
}
通過**我們知道它的過程是這樣的:首先它根據每個timer中的超時差值idx來決定timer所處的tev_base組別tv1~tv5.所以超時事件越後發生,那麼它所處的組位置越靠後。對於tv1組,超時插值idx為0~255之間。差值idx即為所屬組tv1中的陣列下標。從中可知tv1組中相鄰定時器的超時事件間隔1 jiffies發生。對於tv2組,超時差值idx為 256~2^14(16386) 之間,組中相鄰定時時器超事件時間隔256^1 = 256 jiffies發生。以此類推,tv3 組超時差值idx為(16387~2^20)之間,組中相鄰定時器超時時間間隔256^2 = 65536 jiffies發生 … 最後,新增的timer加入到當前節點(超時差值相等)的尾部list_add_tail()形成乙個雙向鍊錶。這樣分組timer雙向鍊錶方便了後面對定時器的遷移更新管理過程,以及最終提高了cpu的處理效率,因為在__run_timers()時,我們只需掃瞄tv1組中即將到來的定時器事件就行了。
我們知道啟動過程時start_kernel()對定時器的初始化是這樣的 :
init_timers() -> run_timer_softirq() -> __run_timers()…
timer_interrupt() -> update_process_times() ->run_local_timers() -> raise_softirq(timer_softirq);
之前我寫的一篇《核心窺秘之一:start_kernel()執行過程記錄》也有提到過.
__run_timers()是系統定時器超時事件的服務程式。這是run_timer_softirq()中一部分,是通過軟中斷的實現的,它是在軟中斷下半部處理的。
static inline void __run_timers(structtvec_base *base)}…
}
對於cascade()函式它是確保之前定時器建立時internal_add_timer()定時器佇列以及佇列租得遷移更新工作,為什麼要遷移,因為,系統在處理定時器時,比較的只是tv1組而已,也就是說,原來的tv1執行完之後,那麼剩下的tv2,tv3,tv4,tv5將會先後遷移到tv1組:tv5 -> tv4 -> tv3 -> tv2-> tv1,這樣定時器超時事件服務程式並不需要對每組的tv的超時事件進行檢測,相比而言,也就提高了cpu的處理效率。那麼這樣一來timer 鍊錶將發生變化,所以需要重新計算,重新實現internal_add_timer(),所以cascade ()函式**如下:
tatic int cascade(struct tvec_base *base,struct tvec *tv, int index)
return index;
}
通過以上分析,我們對linux中系統定時器timer實現過程有所了解了。
TIM基本定時器 定時
定時 輸出比較 輸入捕獲 互補輸出 分類 基本定時器 定時 通用定時器 定時 輸出比較 輸入捕獲 高階定時器 定時 輸出比較 輸入捕獲 互補輸出 f103系列的開發板一般是倆個高階定時器tim1 tim8 4個通用定時器tim2 3 4 5 2個基本定時器tim6 7 而根據容量的大小,又稍有區別。...
linux 核心 核心定時器
一.時鐘中斷概念 1.時鐘中斷由系統的定時硬體以週期性的時間間隔產生,這個間隔 即頻率 由核心根據hz來確定,hz是乙個與體系結構無關的常數,可配置 50 1200 在x86平台上預設值是1000 2.每當時鐘中斷發生的時候,全域性變數jiffies unsigned long 就加1,所以jiff...
linux核心定時器
度量時間差 時鐘中斷由系統的定時硬體以週期性的時間間隔產生,這個間隔 頻率 由核心根據hz來確定,hz是乙個與體系結構無關的常數,可配置 50 1200 在x86平台,預設值為1000.每秒鐘產生1000次時鐘中斷 每當時鐘中斷發生時,全域性變數jiffies就加1,因此其記錄了自linux啟動後時...