定时器是Linux提供的一种定时服务的机制。它在某个特定的时间唤醒某个进程,来做一些工作。Linux初始化时,init_IRQ()函数设定8253的定时周期为10ms(一个tick值)。同样,在初始化时,time_init()用setup_irq()设置时间中断向量irq0,中断服务程序为timer_interrupt。
在2.4版内核及较早的版本当中,定时器的中断处理采用底半机制,底半处理函数的注册在start_kernel()函数中调用sechd_init(),在这个函数中又调用init_bh(TIMER_BH, timer_bh)注册了定时器的底半处理函数。然后系统才调用time_init()来注册定时器的中断向量和中断处理函数。
在中断处理函数timer_interrupt()中,主要是调用do_timer()函数完成工作。do_timer()函数的主要功能就是调用mark_bh()产生软中断,随后处理器会在合适的时候调用定时器底半处理函数timer_bh()。在timer_bh()中,实现了更新定时器的功能。2.4.23版的do_timer()函数代码如下(经过简略):
void do_timer(struct pt_regs *regs)
{
(*(unsigned long *)&jiffies)++;
update_process_times(user_mode(regs));
mark_bh(TIMER_BH);
}
而在内核2.6版本以后,定时器中断处理采用了软中断机制而不是底半机制。时钟中断处理函数仍然为timer_interrup()-> do_timer_interrupt()-> do_timer_interrupt_hook()-> do_timer()。不过do_timer()函数的实现有所不同:
void do_timer(struct pt_regs *regs)
{
jiffies_64++;
update_process_times(user_mode(regs));
update_times();
}
两者所调用的函数基本相同,但是2.4.23版内核与2.6.6内核中,对于update_process_times()函数的实现不同:
void update_process_times(int user_tick)
{
struct task_struct *p = current;
int cpu = smp_processor_id(), system = user_tick ^ 1;
update_one_process(p, user_tick, system, cpu);
run_local_timers();
scheduler_tick(user_tick, system);
}
update_process_times()调用run_local_timers()引起TIMER_SOFTIRQ定时器软中断,处理器在随后的合适的时机运行软中断处理函数run_timer_softirq,这个函数是在init_timers()函数中注册的:
void __init init_timers(void)
{
timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
(void *)(long)smp_processor_id());
register_cpu_notifier(&timers_nb);
open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL);
}
事实上软中断处理函数run_timer_softirq()并没有做什么工作,主要的任务还是通过调用__run_timers()函数完成的,这个函数相当于2.4.23内核当中的run_timer_list()函数的功能。
static inline void __run_timers(tvec_base_t *base)
{
struct timer_list *timer;
spin_lock_irq(&base->lock);
/*这里进入定时器处理循环,利用系统全局jiffies与定时器基准jiffies进行对比,如果前者大,则表明有某些定时器需要进行处理了,否则表示所有的定时器都没有超时*/
while (time_after_eq(jiffies, base->timer_jiffies)) {
struct list_head work_list = LIST_HEAD_INIT(work_list);
struct list_head *head = &work_list;
int index = base->timer_jiffies & TVR_MASK;
/*
在时间列表数据结构当中查找是否存在需要进行超时处理的定时器,时间列表的数据结构定义如下:
typedef struct tvec_s {
struct list_head vec[TVN_SIZE];
} tvec_t;
typedef struct tvec_root_s {
struct list_head vec[TVR_SIZE];
} tvec_root_t;
struct tvec_t_base_s {
spinlock_t lock;
unsigned long timer_jiffies;
struct timer_list *running_timer;
tvec_root_t tv1;
tvec_t tv2;
tvec_t tv3;
tvec_t tv4;
tvec_t tv5;
} ____cacheline_aligned_in_smp;
*/
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_splice_init(base->tv1.vec + index, &work_list);
repeat:
/*如果当前找到的时间数组对应的列表不为空,则表明该列表上串连的所有定时器都已经超时,循环调用每个定时器的处理函数,并将其从列表中删除,直到列表为空为止。*/
if (!list_empty(head)) {
void (*fn)(unsigned long);
unsigned long data;
timer = list_entry(head->next,struct timer_list,entry);
fn = timer->function;
data = timer->data;
list_del(&timer->entry);
set_running_timer(base, timer);
smp_wmb();
timer->base = NULL;
spin_unlock_irq(&base->lock);
fn(data);
spin_lock_irq(&base->lock);
goto repeat;
}
}
set_running_timer(base, NULL);
spin_unlock_irq(&base->lock);
}