初始化本地软时钟

    技术2024-07-15  62

    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

     

    最新回复(0)