3.1.4 定時器處理
static inline void __run_timers(struct tvec_base *base)
{
struct timer_list *timer;
spin_lock_irq(&base->lock);
while (time_after_eq(jiffies, base->timer_jiffies)) {
struct list_head work_list;
struct list_head *head = &work_list;
int index = base->timer_jiffies & TVR_MASK;
/*
* Cascade timers:
*/
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));
++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);
base->running_timer = NULL;
spin_lock_irq(&base->lock);
}
}
wake_up(&base->wait_for_running_timer);
spin_unlock_irq(&base->lock);
}
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);
internal_add_timer(base, timer);
}
return index;
}
當前cpu的tvec_base.timer_jiffies的低8位不爲0時,按tv1-tv5組成的32位數做加法運算來看,tv1到tv2沒有發生進位,所以可以直接處理tvec_base.timer_jiffies的低8位作爲下標索引的tv1中的定時器鏈表中的定時器即可。此時調用定時器的回調函數處理即可。而當tvec_base.timer_jiffies的低8位爲0時,表明發生了進位,此時需要從高一級別的tv2中取出((base->timer_jiffies >> (TVR_BITS + (0) * TVN_BITS)) & TVN_MASK)作爲索引下標的鏈表,使用函數internal_add_timer將鏈表中的數組逐一添加到tv1中。當低14位全爲0時,表明tv1向tv2進位,tv2向tv3進位,此時,先完成tv2向tv1的遷移,在完成tv3到tv2的遷移。後續過程依次類推。
3.1.5 定時器的使用
在使用定時器之前,我們需要知道如何定義定時器。在內核編程中使用定時器,首先我們需要定義一個time_list結構
3.1.5.1 timer_list結構
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;
unsigned long expires;
struct tvec_base *base;
void (*function)(unsigned long);
unsigned long data;
int slack;
.....
}
entry,用於把一組定時器組成一個鏈表。
expires,定時器的到期時刻。也就是定時器到期時刻的jiffies計數值。
base,每個cpu擁有一個自己的用於管理定時器的tvec_base結構。該字段指向定時器所屬cpu所對應的tvec_base結構。
function,函數指針。定時器到期時,系統會調用該回調函數,用於響應定時器的到期事件。
data,function回調函數的參數。
slack,對有些對到期時間精度不太敏感的定時器,到期時刻允許適當的延遲一小段時間。該字段用於計算每次延遲的HZ數。
3.1.5.2 定義timer_list
要定義一個定時器,可以使用靜態和動態兩種方案。靜態方案使用DEFINE_TIMER宏,代碼如下:
#define DEFINE_TIMER(_name, _function, _expires, _data)
該宏將得到一個名字爲_name,回調函數爲_function,回調函數參數爲_data,到期時刻爲_expires的timer_list結構。
如果使用動態方法,可以自己聲明一個timer_list結構,之後手動初始化其相關字段,代碼如下:
struct timer_list timer;
init_timer(&timer);
timer.function=_function;
timer.expires=_expires;
timer.data=_data;
3.1.5.3 激活定時器
可以使用add_timer(&timer)激活一個定時器。
3.1.5.4 修改定時器到期時間
可以使用mod_timer(&timer, new_expires)修改定時器到期時間。
3.1.5.5 移除定時器
使用函數del_timer(&timer)移出定時器。
3.1.5.6 延遲處理
對於某些對精度不敏感的定時器,我們可以設定timer_list.slack字段的值,設定timer允許到期時刻的最大延遲。使用函數set_timer_slack(&timer, slack_hz)可以完成目標。