概述
Linux的內核定時器依賴於內核軟中斷,當系統硬件中斷退出時會便利軟件中斷的使能位並執行其關聯的回掉函數
//內核定時器初始化會打開內核TIMER_SOFTIRQ軟中斷,執行時會執行到run_timer_softirq函數
void __init init_timers(void) { open_softirq(TIMER_SOFTIRQ, run_timer_softirq) }
//run_timer_softirq函數分析
void run_timer_softirq(struct softirq_action *h)
{
struct tvec_base *base = this_cpu_ptr(&tvec_bases);
struct tvec_base是個Per-CPU變量,只在本地CPU有效,
void __run_timers(struct tvec_base *base)
{
spin_lock_irq(&base->lock)
//找到超時的timer,並將其賦值給base->running_timer,這個值會在多處理器刪除時使用
base->running_timer = timer
detach_expired_timer(timer, base)
//將timer從base鏈表中刪除,我們可以看到一個定時器如果沒有在鏈表中,不能說明它已經執行完畢
spin_unlock_irq(&base->lock)
call_timer_fn(timer, fn, timer->data)
spin_lock_irq(&base->lock)
base->running_timer = NULL
spin_unlock_irq(&base->lock)
}
return __run_timers(base);
}
定義定時器
//方法1 DEFINE_TIMER(timer, cb, expires, data) //方法2 struct timer_list timer; setup_timer(&timer, (*cb)(data), data) timer.expires = jiffies + 5*HZ //方法3 struct timer_list timer init_timer(&timer) timer.timer.expires = jiffies + 5*HZ timer.timer.data = data timer.timer.function = cb
添加定時器
//添加定時器是將一個定時器掛到所在鏈表中,我們可以看到,其最終還是調用了__mod_timer函數。
//__mod_timer函數是mod_timer的底層調用,我們在后面分析
void add_timer(struct timer_list *timer) { __mod_timer(timer, timer->expires) }
刪除定時器
//通過del_timer函數注銷一個定時器,如果定時器存在返回1,否則返回0
//del_timer_sync是用在SMP系統中的,在單核系統中和del_timer是一樣的,
//del_timer_sync會判斷當前定時器是否在其他CPU上運行,如果運行等待其運行完成后刪除此定時器
int del_timer(struct timer_list *timer) int del_timer_sync(struct timer_list *timer)
定時器掛起
//用於判斷一個定時器是否存在在鏈表中,但是此定時器有可能正在運行
int timer_pending(struct timer_list *timer)
函數分析
int __mod_timer(struct timer_list *timer) { base = lock_timer_base(timer) //獲取tvec_base變量,我們知道base為PerCPU變量,這個函數也順便給base加鎖。 if (timer_pending(timer)) { deteach_timer(timer, 0) ret = 1 } //如果timer已經存在,則刪除,此處的操作是在加鎖狀態先的,因此和softirq里面不會重入 new_base = __get_cpu_var(tvec_base) if (base != new_base) { //我們不能修改一個正在運行的timer的base,否則del_timer_sync會出問題,后面我們再說這個。 if (base->running_timer != timer) { timer->base =NULL; //由於本地中斷已經在76行處關閉,因此不開本地中斷 spin_unlock(&base->lock) base = new_base; //由於前面已經禁用了本地中斷,因此這只需加鎖 spin_lock(&base->lock) timer->base = base } } //如上代碼如果當前timer base不是timer->base則更新timer->base internal_add_timer(base, timer) //將timer加入本地base中 spin_unlock_irqrestore(&base->lock, flag) //1,釋放鎖 //2,恢復中斷 //3,啟用內核搶占 //由以上代碼我們可以知道,同一個timer只能被提交一次, //但是同一個timer是有可能在不同的cpu上同時運行的,我們可以試想這個一個場景, //CPU0注冊一個timer,並已經在CPU0上運行了, //CPU1也注冊了這個timer,因為運行的timer已經在鏈表上刪除了且沒有獲取base->lock, //因此lock_timer_base和timer_pending都可以向下執行並注冊成功 //假設此timer的時間非常短,CPU1的timer在注冊完成后由於系統的一次中斷立刻執行, //此時同一個timer在CPU0和CPU1並發 //以上是個人的一個理解,如果錯誤,還請指出 } //由以上可以知道,在哪個CPU運行增加timer的代碼,則timer的回掉函哪個CPU上運行。 del_timer(struct timer_list *timer) { if (timer_pending(timer)) { base = lock_timer_base(timer, &flag); ret = detach_if_pending(timer, base, true); spin_unlock_irqrestore(&base->lock, flag); } return ret //由此函數我們可以知道,此函數只判斷timer是否在鏈表中,然后就直接刪除,但是刪除時此函數的回掉有可能正在運行, //在判斷timer_pending(timer)時,理論上來說應該加鎖保護,但是此處沒有加鎖保護,是因為在detach_if_pending中會再次判斷是否pending如果pending則函數直接退出, } del_timer_sync(struct timer_list *timer) { for(;;) { int try_to_del_timer_sync(timer) { base = lock_timer_base(timer, &flag); //判斷當前timer是否在運行,如果在運行則等待 if (base->running_timer != timer) { //detach_if_pending如果timer pending返回0 //如果timer刪除成功返回1 ret = detach_if_pending(timer, base, true); } spin_unlock_irqrestore(&base->lock, flags) return ret; } //如果timer刪除成功返回0或者1,負責等待刪除 int ret = try_to_del_timer_sync(timer); if (ret >= 0) return ret; cpu_relax(); } }