void __init init_timers(void) { //初始化本 CPU 上的软件时钟相关的数据结构 int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE, (void *)(long)smp_processor_id()); //因为是初始化阶段,所以得到的CPU为启动CPU init_timer_stats(); BUG_ON(err != NOTIFY_OK); //向 cpu_chain 通知链注册元素 timers_nb ,该元素的回调函数用于初始化指定 CPU 上的软件时钟相关的数据结构 register_cpu_notifier(&timers_nb); //初始化时钟的软中断处理函数 open_softirq(TIMER_SOFTIRQ, run_timer_softirq); }
这个函数完成的主要作用包括:
(1)初始化本 CPU 上的软件时钟相关的数据结构;
(2)向 cpu_chain 通知链注册元素 timers_nb ,该元素的回调函数用于初始化指定 CPU 上的软件时钟相关的数据结构;
(3)初始化时钟的软中断处理函数。
对于操作(1):
static int __cpuinit timer_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { long cpu = (long)hcpu; int err; switch(action) { case CPU_UP_PREPARE: case CPU_UP_PREPARE_FROZEN: err = init_timers_cpu(cpu); //调用该函数,参数CPU即为启动CPU(或者主CPU) if (err < 0) return notifier_from_errno(err); break; #ifdef CONFIG_HOTPLUG_CPU case CPU_DEAD: case CPU_DEAD_FROZEN: migrate_timers(cpu); break; #endif default: break; } return NOTIFY_OK; }
调用init_timers_cpu:
static int __cpuinit init_timers_cpu(int cpu) { int j; struct tvec_base *base; static char __cpuinitdata tvec_base_done[NR_CPUS]; if (!tvec_base_done[cpu]) { //启动CPU尚未进行tvec的设置 static char boot_done; if (boot_done) { /* * The APs use this path later in boot */ base = kmalloc_node(sizeof(*base), GFP_KERNEL | __GFP_ZERO, cpu_to_node(cpu)); if (!base) return -ENOMEM; /* Make sure that tvec_base is 2 byte aligned */ if (tbase_get_deferrable(base)) { WARN_ON(1); kfree(base); return -ENOMEM; } per_cpu(tvec_bases, cpu) = base; } else { //第一次进行设置 /* * This is for the boot CPU - we use compile-time * static initialisation because per-cpu memory isn't * ready yet and because the memory allocators are not * initialised either. */ boot_done = 1; base = &boot_tvec_bases; } tvec_base_done[cpu] = 1; } else { base = per_cpu(tvec_bases, cpu); } spin_lock_init(&base->lock); //开始初始化5个定时器表 for (j = 0; j < TVN_SIZE; j++) { INIT_LIST_HEAD(base->tv5.vec + j); INIT_LIST_HEAD(base->tv4.vec + j); INIT_LIST_HEAD(base->tv3.vec + j); INIT_LIST_HEAD(base->tv2.vec + j); } for (j = 0; j < TVR_SIZE; j++) INIT_LIST_HEAD(base->tv1.vec + j); //默认值为初始化时的jiffes base->timer_jiffies = jiffies; //当前正在处理的软件时钟到期时间 base->next_timer = base->timer_jiffies; return 0; }
对于操作(3),open_softirq(TIMER_SOFTIRQ, run_timer_softirq):
void open_softirq(int nr, void (*action)(struct softirq_action *)) { softirq_vec[nr].action = action; }
我们看到,定时器软中断所对应的action是run_timer_softirq,也就是当时钟中断到来,软中断启动时,就会调用这个函数,我们来看一下这个函数:
/* * This function runs timers and the timer-tq in bottom half context. */ static void run_timer_softirq(struct softirq_action *h) { struct tvec_base *base = __this_cpu_read(tvec_bases); hrtimer_run_pending(); //判断当前的jiffies是否大于等于最小的那个超时jiffies.是的话就进入定时器处理 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(struct tvec_base *base) { struct timer_list *timer; spin_lock_irq(&base->lock); while (time_after_eq(jiffies, base->timer_jiffies)) { //处理所有从时间点timer_jiffies 到 时间点jiffies的事件。 struct list_head work_list; struct list_head *head = &work_list; int index = base->timer_jiffies & TVR_MASK; //计算第一组的索引位置 /* * Cascade timers: */ if (!index && (!cascade(base, &base->tv2, INDEX(0))) && //cascade用于从指定组取得定时器补充前一组。 (!cascade(base, &base->tv3, INDEX(1))) && !cascade(base, &base->tv4, INDEX(2))) cascade(base, &base->tv5, INDEX(3)); //如果前组都已经是空的了,那么就将第五组的向前移动(因为第五组的时间到期时间实在是太晚,因此一般都不会东它们。) ++base->timer_jiffies; //timer_jiffiers记录的是一个时间点,这个时间点之前到期的定时器都已经处理过了。 list_replace_init(base->tv1.vec + index, &work_list); //第一组位于索引位置的所有定时器都转移到一个临时链表中,从原来的数据结构中删除。 while (!list_empty(head)) { //分别执行各个定时器的处理程序 void (*fn)(unsigned long); unsigned long data; timer = list_first_entry(head, struct timer_list,entry); fn = timer->function; data = timer->data; timer_stats_account_timer(timer); base->running_timer = timer; detach_timer(timer, 1); spin_unlock_irq(&base->lock); call_timer_fn(timer, fn, data); spin_lock_irq(&base->lock); } } base->running_timer = NULL; spin_unlock_irq(&base->lock); }