轉自:http://www.lampbrother.net/php/bencandy.php?fid=42&id=230
在 Linux 操作系統中,很多活動都和時間有關,本文分析了 Linux 2.6.25 內核的時鐘處理機制,首先介紹了在計算機系統中的一些硬件計時器,然後重點介紹了 Linux 操作系統中的硬件時鐘和軟件時鐘的處理過程以及軟件時鐘的應用。
時鐘的軟中斷處理
軟件時鐘的處理是在時鐘的軟中斷中進行的。
軟中斷初始化
軟中斷的一個重要的處理時機是在每個硬件中斷處理完成後(參見 irq_exit 函數),且由2.4節的內容可知:在硬件時鐘中斷處理中,會喚醒時鐘的軟中斷,所以每次硬件時鐘中斷處理函數執行完成後都要進行時鐘的軟中斷處理。和時鐘 相關的軟中斷是
TIMER_SOFTIRQ ,其處理函數爲 run_timer_softirq ,該函數用來處理所有的軟件時鐘。這部分初始化代碼在函數 init_timers 中進行,如清單3-7
1 |
void
__init init_timers(void) |
4 |
open_softirq(TIMER_SOFTIRQ,
run_timer_softirq, NULL); |
處理過程
函數 run_timer_softirq 所作的工作就是找出所有到期的軟件時鐘,然後依次執行其處理函數。其代碼如清單3-8
1 |
static void
run_timer_softirq(struct softirq_action *h) |
3 |
struct
tvec_base *base = __get_cpu_var(tvec_bases); |
6 |
if (time_after_eq(jiffies,
base->timer_jiffies)) |
函數首先獲得到本地 CPU 的 base 。然後檢測如果 jiffies大於等於 timer_jiffies ,說明可能已經有軟件時鐘到期了,此時就要進行軟件時鐘的處理,調用函數 __run_timers 進行處理。如果 jiffies
小於 timer_jiffies ,表明沒有軟件時鐘到期,則不用對軟件時鐘進行處理。函數返回。
注: hrtimer_run_pending() 函數是高精度時鐘的處理。本文暫沒有涉及高精度時鐘相關的內容。
|
接下來看一下函數 __run_timers 都作了些什麼,如清單3-9
01 |
static inline
void __run_timers(struct tvec_base *base) |
04 |
spin_lock_irq(&base->lock); |
05 |
while (time_after_eq(jiffies,
base->timer_jiffies)) { |
07 |
int
index = base->timer_jiffies & TVR_MASK; |
09 |
(!cascade(base,
&base->tv2, INDEX(0))) && |
10 |
(!cascade(base,
&base->tv3, INDEX(1))) && |
11 |
!cascade(base,
&base->tv4, INDEX(2))) |
12 |
cascade(base,
&base->tv5, INDEX(3)); |
13 |
++base->timer_jiffies; |
14 |
list_replace_init(base->tv1.vec
+ index, &work_list); |
15 |
while (!list_empty(head))
{ |
17 |
timer
= list_first_entry(head, struct timer_list,entry); |
21 |
set_running_timer(base,
timer); |
22 |
detach_timer(timer,
1); |
23 |
spin_unlock_irq(&base->lock); |
25 |
int
preempt_count = preempt_count(); |
29 |
spin_lock_irq(&base->lock); |
32 |
set_running_timer(base,
NULL); |
33 |
spin_unlock_irq(&base->lock); |
代碼解釋:
- 獲得 base 的同步鎖
- 如果 jiffies 大於等於 timer_jiffies (當前正要處理的軟件時鐘的到期時間,說明可能有軟件時鐘到期了),就一直運行3~7,否則跳轉至8
- 計算得到 tv1 的索引,該索引指明當前到期的軟件時鐘所在 tv1 中的鏈表,代碼:
1 |
int
index = base->timer_jiffies & TVR_MASK; |
- 調用 cascade 函數對軟件時鐘進行必要的調整(稍後會介紹調整的過程)
- 使得 timer_jiffies 的數值增加1
- 取出相應的軟件時鐘鏈表
- 遍歷該鏈表,對每個元素進行如下操作
- 設置當前軟件時鐘爲 base 中正在運行的軟件時鐘(即保存當前軟件時鐘到 base-> running_timer 成員中)
- 將當前軟件時鐘從鏈表中刪除,即卸載該軟件時鐘
- 釋放鎖,執行軟件時鐘處理程序
- 再次獲得鎖
- 設置當前 base 中不存在正在運行的軟件時鐘
- 釋放鎖