struct timer_list :軟體時鐘,記錄了軟體時鐘的到期時間以及到期後要執行的操作。具體的成員以及含義見表3-1。
struct tvec_base :用於組織、管理軟體時鐘的結構。在 SMP 系統中,每個 CPU 有一個。具體的成員以及含義參見表3-2。
表3-1 struct timer_list 主要成員
網域名稱 類型 描述
entry struct list_head 所在的鏈表
expires unsigned long 到期時間,以 tick 為單位
function void (*)(unsigned long) 回呼函數,到期後執行的操作
data unsigned long 回呼函數的參數
base struct tvec_base * 記錄該軟體時鐘所在的 struct tvec_base 變數
表3-2 struct tvec_base 類型的成員
網域名稱 類型 描述
lock spinlock_t 用於同步操作
running_timer struct timer_list * 正在處理的軟體時鐘
timer_jiffies unsigned long 當前正在處理的軟體時鐘到期時間
tv1 struct tvec_root 儲存了到期時間從 timer_jiffies 到 timer_jiffies + 淺析Linux 時鐘處理機制(圖二)之間(包括邊緣值)的所有軟體時鐘
tv2 struct tvec 儲存了到期時間從 timer_jiffies + 淺析Linux 時鐘處理機制(圖三)到 timer_jiffies +淺析Linux 時鐘處理機制(圖四)之間(包括邊緣值)的 所有軟體時鐘
tv3 struct tvec 儲存了到期時間從 timer_jiffies +淺析Linux 時鐘處理機制(圖五)到 timer_jiffies +淺析Linux 時鐘處理機制(圖六)之間(包括邊緣值)的所有軟體時鐘
tv4 struct tvec 儲存了到期時間從 timer_jiffies +淺析Linux 時鐘處理機制(圖七)到 timer_jiffies +淺析Linux 時鐘處理機制(圖八)之間(包括邊緣值)的所有軟體時鐘
tv5 struct tvec 儲存了到期時間從 timer_jiffies +淺析Linux 時鐘處理機制(圖九)到 timer_jiffies +淺析Linux 時鐘處理機制(圖十)之間(包括邊緣值)的所有軟體時鐘
其中 tv1 的類型為 struct tvec_root ,tv 2~ tv 5的類型為 struct tvec ,清單3-1顯示它們的定義
清單3-1 struct tvec_root 和 struct tvec 的定義
struct tvec {
struct list_head vec[TVN_SIZE];
};
struct tvec_root {
struct list_head vec[TVR_SIZE];
};
可見它們實際上就是類型為 struct list_head 的數組,其中 TVN_SIZE 和 TVR_SIZE 在系統沒有配置宏 CONFIG_BASE_SMALL 時分別被定義為64和256。
/*
* This function runs timers and the timer-tq in bottom half context.
*/
static void run_timer_softirq(struct softirq_action *h)
{
tvec_base_t *base = __get_cpu_var(tvec_bases);
hrtimer_run_queues();
//首先在非強制中斷處理中判斷jiffies如果大於最近的定時器的到期時間base->timer_jiffies,就表示有定時器到期了
//進入__run_timers(base)進行處理
if (time_after_eq(jiffies, base->timer_jiffies))
__run_timers(base);
}
/**
* __run_timers - run all expired timers (if any) on this CPU.
* @base: the timer vector to be processed.
*
* This function cascades all vectors and executes all expired timer
* vectors.
*/
static inline void __run_timers(tvec_base_t *base)
{
struct timer_list *timer;
spin_lock_irq(&base->lock);
//判斷jiffies是否大於當前定時器到期時間base->timer_jiffies,
//base->timer_jiffies也就是到期時間最短的那個
while (time_after_eq(jiffies, base->timer_jiffies)) {
struct list_head work_list;
struct list_head *head = &work_list;
//最近定時器在tv1中的index,也就是jiffies的低6位
int index = base->timer_jiffies & TVR_MASK;
/*
* Cascade timers:
*/
//如果tv1中index==0,表示tv1中定時器全部過時了,需要從tv2~tv5中取出256個jiffies的定時器填充tv1
//也就是說每過256個tick,就會cascade一次,下面再介紹cascade
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));
//為什麼加1?
++base->timer_jiffies;
//tv1在index位置的定時器隊列,用work_list指向它,並把tv1中index的隊列頭設為空白
list_replace_init(base->tv1.vec + index, &work_list);
//如果取出的定時器隊列不為空白,需要處理定時器
while (!list_empty(head)) {
void (*fn)(unsigned long);
unsigned long data;
//找第一個timer
timer = list_first_entry(head, struct timer_list,entry);
fn = timer->function;
data = timer->data;
timer_stats_account_timer(timer);
set_running_timer(base, timer); //設為正在啟動並執行timer
detach_timer(timer, 1); //刪除這個timer
spin_unlock_irq(&base->lock); //在執行timer回呼函數時,先釋放lock
{
int preempt_count = preempt_count();
fn(data); //執行timer的函數
if (preempt_count != preempt_count()) {
printk(KERN_WARNING "huh, entered %p "
"with preempt_count %08x, exited"
" with %08x?\n",
fn, preempt_count,
preempt_count());
BUG();
}
}
spin_lock_irq(&base->lock);
}
}
set_running_timer(base, NULL); //把正在啟動並執行timer設為空白
spin_unlock_irq(&base->lock);
}
//取出tv2~tv5的定時器,放到tv1中
static int cascade(tvec_base_t *base, tvec_t *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;
}
//tv2對應INDEX(0),即(jiffies>>8)&0x3f
//tv3對應INDEX(1),即(jiffies>>14)&0x3f
//tv4對應INDEX(2),即(jiffies>>20)&0x3f
//tv5對應INDEX(3),即(jiffies>>26)&0x3f
#define INDEX(N) ((base->timer_jiffies >> (TVR_BITS + (N) * TVN_BITS)) & TVN_MASK)