转自: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 中不存在正在运行的软件时钟
- 释放锁