初始化本地软时钟

5.10.2 初始化本地软时钟

native_init_IRQ结束后,init_IRQ也就结束了,回到start_kernel中,607行,prio_tree_init函数很简单:

 

void __init prio_tree_init(void)

{

       unsigned int i;

 

       for (i = 0; i < ARRAY_SIZE(index_bits_to_maxindex) - 1; i++)

              index_bits_to_maxindex[i] = (1UL << (i + 1)) - 1;

       index_bits_to_maxindex[ARRAY_SIZE(index_bits_to_maxindex) - 1] = ~0UL;

}

 

初始化全局变量index_bits_to_maxindex[]数组,不在话下。继续走,608行,init_timers,来自kernel/timer.c

 

1736void __init init_timers(void)

1737{

1738        int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,

1739                                (void *)(long)smp_processor_id());

1740

1741        init_timer_stats();

1742

1743        BUG_ON(err != NOTIFY_OK);

1744        register_cpu_notifier(&timers_nb);

1745        open_softirq(TIMER_SOFTIRQ, run_timer_softirq);

1746}

 

init_timers函数首先1738初始化本地CPU上的软时钟相关的数据结构,该结构是一个notifier_block类型全局变量,定义在kernel/time.c

static struct notifier_block __cpuinitdata timers_nb = {

       .notifier_call   = timer_cpu_notify,

};

 

timers_nb的回调函数timer_cpu_notify用于初始化指定CPU上的软时钟相关的数据结构:

 

 

static int __cpuinit timer_cpu_notify(struct notifier_block *self,

                            unsigned long action, void *hcpu)

{

       long cpu = (long)hcpu;

       switch(action) {

       case CPU_UP_PREPARE:

       case CPU_UP_PREPARE_FROZEN:

              if (init_timers_cpu(cpu) < 0)

                     return NOTIFY_BAD;

              break;

#ifdef CONFIG_HOTPLUG_CPU

       case CPU_DEAD:

       case CPU_DEAD_FROZEN:

              migrate_timers(cpu);

              break;

#endif

       default:

              break;

       }

       return NOTIFY_OK;

}

 

我们指定的是CPU_UP_PREPARE,所以肯定执行init_timers_cpu(cpu)

 

1513static int __cpuinit init_timers_cpu(int cpu)

1514{

1515        int j;

1516        struct tvec_base *base;

1517        static char __cpuinitdata tvec_base_done[NR_CPUS];

1518

1519        if (!tvec_base_done[cpu]) {

1520                static char boot_done;

1521

1522                if (boot_done) {

1523                        /*

1524                         * The APs use this path later in boot

1525                         */

1526                        base = kmalloc_node(sizeof(*base),

1527                                                GFP_KERNEL | __GFP_ZERO,

1528                                                cpu_to_node(cpu));

1529                        if (!base)

1530                                return -ENOMEM;

1531

1532                        /* Make sure that tvec_base is 2 byte aligned */

1533                        if (tbase_get_deferrable(base)) {

1534                                WARN_ON(1);

1535                                kfree(base);

1536                                return -ENOMEM;

1537                        }

1538                        per_cpu(tvec_bases, cpu) = base;

1539                } else {

1540                        /*

1541                         * This is for the boot CPU - we use compile-time

1542                         * static initialisation because per-cpu memory isn't

1543                         * ready yet and because the memory allocators are not

1544                         * initialised either.

1545                         */

1546                        boot_done = 1;

1547                        base = &boot_tvec_bases;

1548                }

1549                tvec_base_done[cpu] = 1;

1550        } else {

1551                base = per_cpu(tvec_bases, cpu);

1552        }

1553

1554        spin_lock_init(&base->lock);

1555

1556        for (j = 0; j < TVN_SIZE; j++) {

1557                INIT_LIST_HEAD(base->tv5.vec + j);

1558                INIT_LIST_HEAD(base->tv4.vec + j);

1559                INIT_LIST_HEAD(base->tv3.vec + j);

1560                INIT_LIST_HEAD(base->tv2.vec + j);

1561        }

1562        for (j = 0; j < TVR_SIZE; j++)

1563                INIT_LIST_HEAD(base->tv1.vec + j);

1564

1565        base->timer_jiffies = jiffies;

1566        base->next_timer = base->timer_jiffies;

1567        return 0;

1568}

 

1517定义个全局变量tvec_base_done[]数组,每个元素对应一个CPU的软时钟初始化状态。随后1519~1552行创建本地软时钟数据结构tvec_base。该结构定义如下:

struct tvec_base {

       spinlock_t lock;

       struct timer_list *running_timer;

       unsigned long timer_jiffies;

       unsigned long next_timer;

       struct tvec_root tv1;

       struct tvec tv2;

       struct tvec tv3;

       struct tvec tv4;

       struct tvec tv5;

} ____cacheline_aligned;

 

然后1556~1566行初始化这个tvec_base结构。我们看到,最重要的是1565行,tvec_base结构的timer_jiffies字段被设置成了超级重要的jiffies宏,即自系统启动以来产生的节拍的总数,通过如下函数获得(本质上是一个汇编指令syscall):

# define jiffies       raid6_jiffies()

static inline uint32_t raid6_jiffies(void)

{

       struct timeval tv;

       gettimeofday(&tv, NULL);

       return tv.tv_sec*1000 + tv.tv_usec/1000;

}

static __always_inline int gettimeofday(struct timeval *tv, struct timezone *tz)

{

       int ret;

       asm volatile("syscall"

              : "=a" (ret)

              : "0" (__NR_gettimeofday),"D" (tv),"S" (tz)

              : __syscall_clobber );

       return ret;

}

 

回到init_timers,当初始化了本地CPU上的软时钟数据结构之后,1741行,调用init_timer_stats,初始化每CPU变量tstats_lookup_lock作为自旋锁。然后1744行调用我们已经见过的register_cpu_notifier函数,将新建的这个timers_nb结构挂到全局cpu_chain链中,作为通知链注册。

 

最后,1655行,调用open_softirq初始化时钟的软中断处理函数:

void open_softirq(int nr, void (*action)(struct softirq_action *))

{

       softirq_vec[nr].action = action;

}

 

软中断的概念如果还不熟悉的,请参考博客“下半部分”

http://blog.csdn.net/yunsongice/archive/2010/03/07/5354011.aspx

 

你可能感兴趣的:(数据结构,timer,list,timezone,struct,action)