低精度定時器的API
初始化定時器:
void init_timer(struct timer_list * timer);
增加定時器:
void add_timer(struct timer_list * timer);
刪除定時器:
int del_timer(struct timer_list * timer);
修改定時器的expire:
int mod_timer(struct timer_list *timer, unsigned long expires);
使用示例
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/timer.h>
struct timer_list mytimer;//定義一個定時器
void mytimer_func(unsigned long data)
{
printk("timer data: %d\n", data);
}
static int __init test_init (void)
{
printk("test init\n");
init_timer(&mytimer); //初始化定時器
mytimer.expires = jiffies + 100;//設定超時時間100代表1秒
mytimer.data = 200; //傳遞給定時器超時函數的值
mytimer.function = mytimer_func;//設置定時器超時函數
add_timer(&mytimer); //添加定時器,定時器開始生效
return 0;
}
static void __exit test_exit (void)
{
del_timer(&mytimer);//卸載模塊時,刪除定時器
printk("test exit\n");
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
實現機制
低精度定時器的實現機制是時間輪算法。
每一個CPU維護一個tevc_base結構,爲這個CPU的時間輪
static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases;
tevc_base結構:
struct tvec_base {
spinlock_t lock;
struct timer_list *running_timer;
unsigned long timer_jiffies;
unsigned long next_timer;
struct tvec_root tv1;
struct tvec tv2;
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
};
running_timer 該字段指向當前cpu正在處理的定時器所對應的timer_list結構。
next_timer 該字段指向該cpu下一個即將到期的定時器。
tv1~5代表5個wheel。
tv1是轉速最快的wheel,TVR_SIZE(256)個jiffies內到期的定時器都會掛在tv1的某個鏈表頭中。
tv2是轉速第二快的wheel,裏面掛的定時器超時jiffies在2^8 ~ 2^(8+6)之間。
tv3是轉速第三快的wheel,超時jiffies在2^(8+6) ~ 2^(8+2*6)之間。
tv4、tv5以此類推。
tv1~tv5的size分別爲256,64,64,64,64,表示的範圍爲256+64+64+64+64 = 2^32.
下圖爲時間輪的結構:
內核實現:
(參考博文:http://blog.csdn.net/walkingman321/article/details/6101536)
add_timer是__mod_timer函數的一個封裝,__mod_timer最終會調用internal_add_timer。
static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
{
unsigned long expires = timer->expires;
unsigned long idx = expires - base->timer_jiffies; // idx爲定時器距離超時還有多少jiffies
struct list_head *vec;
// 如果idx少於TVR_SIZE(256),把定時器加入tv1
if (idx < TVR_SIZE) {
int i = expires & TVR_MASK;
vec = base->tv1.vec + i;
}
// 如果idx少於2^(8+6)次方,加入tv2
else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
int i = (expires >> TVR_BITS) & TVN_MASK;
vec = base->tv2.vec + i;
}
// 類推,加入tv3
else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
vec = base->tv3.vec + i;
}
// 類推,加入tv4
else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
vec = base->tv4.vec + i;
}
// 如果idx < 0,說明定時器已經超時,應該馬上被調度,把它加入base->timer_jiffies,
// 即base的當前jiffies對應的隊列。這裏要注意的是,這個函數在spin_lock_irq被調
// 用之後調用,所以不會與定時器調度函數產生競態。
else if ((signed long) idx < 0) {
/*
* Can happen if you add a timer with expires == jiffies,
* or you set a timer to Go off in the past
*/
vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);
}
// 加入tv5隊列。
else {
int i;
/* If the timeout is larger than 0xffffffff on 64-bit
* architectures then we use the maximum timeout:
*/
if (idx > 0xffffffffUL) {
idx = 0xffffffffUL;
expires = idx + base->timer_jiffies;
}
i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
vec = base->tv5.vec + i;
}
trace_timer_set(timer);
/*
* Timers are FIFO:
*/
list_add_tail(&timer->entry, vec);
}
定時器加入tv1中使用的下標索引是定時器到期時間expires的低8位,所以假設當前的base.timer_jiffies值是0x34567826,則馬上到期的定時器是在tv1.vec[0x26]中,如果這時候系統加入一個在jiffies值0x34567828到期的定時器,他將會加入到tv1.vec[0x28]中,運行兩個tick後,base.timer_jiffies的值會變爲0x34567828,很顯然,在每次tick事件中,定時器系統只要以base.timer_jiffies的低8位作爲索引,取出tv1中相應的鏈表,裏面正好包含了所有在該jiffies值到期的定時器列表。
當jiffies的低8位爲0時,tv1到了觸發點,調用cascade函數把tv2的某一格定時器搬到tv1。tv2的cascade函數返回0時,代表tv2也到了觸發點,於是又觸發下一級時間輪tv3。依此類推。
static int cascade(struct tvec_base *base, struct tvec *tv, int index)
{
/* cascade all the timers from tv up one level */
struct timer_list *timer, *tmp;
struct list_head tv_list;
list_replace_init(tv->vec + index, &tv_list); // 移除需要遷移的鏈表
/*
* We are removing _all_ timers from the list, so we
* don't have to detach them individually.
*/
list_for_each_entry_safe(timer, tmp, &tv_list, entry) {
BUG_ON(tbase_get_base(timer->base) != base);
// 重新加入到定時器系統中,實際上將會遷移到下一級的tv數組中
internal_add_timer(base, timer);
}
return index;
}
timer的調度在軟中斷中完成,在init_timers函數中調用
open_softirq(TIMER_SOFTIRQ, run_timer_softirq)
註冊定時器對應的軟中斷處理函數。
static void run_timer_softirq(struct softirq_action *h)
{
struct tvec_base *base = __get_cpu_var(tvec_bases);
hrtimer_run_pending(); // 高精度時鐘的處理
// 如果jiffies超過了base->timer_jiffies,那麼調用時鐘處理函數
if (time_after_eq(jiffies, base->timer_jiffies))
__run_timers(base);
}
run_timers
static inline void __run_timers(struct tvec_base *base)
{
struct timer_list *timer;
spin_lock_irq(&base->lock);
/* 同步jiffies,在NO_HZ情況下,base->timer_jiffies可能落後不止一個tick */
while (time_after_eq(jiffies, base->timer_jiffies)) {
struct list_head work_list;
struct list_head *head = &work_list;
/* 計算到期定時器鏈表在tv1中的索引 */
int index = base->timer_jiffies & TVR_MASK;
/*
* tv2--tv5定時器列表遷移處理
*/
if (!index &&
(!cascade(base, &base->tv2, INDEX(0))) &&
(!cascade(base, &base->tv3, INDEX(1))) &&
!cascade(base, &base->tv4, INDEX(2)))
cascade(base, &base->tv5, INDEX(3));
/* 該cpu定時器系統運行時間遞增一個tick */
++base->timer_jiffies;
/* 取出到期的定時器*/
list_replace_init(base->tv1.vec + index, &work_list);
/* 遍歷所有的到期定時器 */
while (!list_empty(head)){
void (*fn)(unsigned long);
unsigned long data;
timer = list_first_entry(head, struct timer_list,entry);
fn = timer->function;
data = timer->data;
timer_stats_account_timer(timer);
base->running_timer = timer; /* 標記正在處理的定時器 */
detach_timer(timer, 1);
spin_unlock_irq(&base->lock);
call_timer_fn(timer, fn, data); /* 調用定時器的回調函數 */
spin_lock_irq(&base->lock);
}
}
base->running_timer = NULL;
spin_unlock_irq(&base->lock);
}