• Linux内核(12)


    内核选项的解析完成之后,各个子系统的初始化即进入第二部分—入口函数的调用。通常USB、PCI这样的子系统都会有一个名为subsys_initcall的入口,如果你选择它们作为研究内核的切入点,那么就请首先找到它。

    朱德庸在《关于上班这件事》里说,要花前半生找入口,花后半生找出口。可见寻找入口对于咱们这一生,对于看内核代码这件事儿都是无比重要的。

    但是很多时候,入口并不仅仅只有subsys_initcall一个,比如PCI。

    117 #define pure_initcall(fn)               __define_initcall("0",fn,1)
    118
    119 #define core_initcall(fn)               __define_initcall("1",fn,1)
    120 #define core_initcall_sync(fn)          __define_initcall("1s",fn,1s)
    121 #define postcore_initcall(fn)           __define_initcall("2",fn,2)
    122 #define postcore_initcall_sync(fn)      __define_initcall("2s",fn,2s)
    123 #define arch_initcall(fn)               __define_initcall("3",fn,3)
    124 #define arch_initcall_sync(fn)          __define_initcall("3s",fn,3s)
    125 #define subsys_initcall(fn)             __define_initcall("4",fn,4)
    126 #define subsys_initcall_sync(fn)        __define_initcall("4s",fn,4s)
    127 #define fs_initcall(fn)                 __define_initcall("5",fn,5)
    128 #define fs_initcall_sync(fn)            __define_initcall("5s",fn,5s)
    129 #define rootfs_initcall(fn)     __define_initcall("rootfs",fn,rootfs)
    130 #define device_initcall(fn)             __define_initcall("6",fn,6)
    131 #define device_initcall_sync(fn)        __define_initcall("6s",fn,6s)
    132 #define late_initcall(fn)               __define_initcall("7",fn,7)
    133 #define late_initcall_sync(fn)          __define_initcall("7s",fn,7s)
    134
    135 #define __initcall(fn) device_initcall(fn)

    这些入口有个共同的特征,它们都是使用__define_initcall宏定义的。它们的调用也不是随便的,而是按照一定顺序的,这个顺序就取决于__define_initcall宏。__define_initcall宏用来将指定的函数指针放到.initcall.init节里。

    .initcall.init节

    内核可执行文件由许多链接在一起的对象文件组成。对象文件有许多节,如文本、数据、init数据、bass等等。这些对象文件都是由一个称为链接器脚本的文件链接并装入的。这个链接器脚本的功能是将输入对象文件的各节映射到输出文件中;换句话说,它将所有输入对象文件都链接到单一的可执行文件中,将该可执行文件的各节装入到指定地址处。 vmlinux.lds是存在于arch/<target>/目录中的内核链接器脚本,它负责链接内核的各个节并将它们装入内存中特定偏移量处。在vmlinux.lds文件里查找initcall.init就可以看到下面的内容

    __inicall_start = .;
    .initcall.init : AT(ADDR(.initcall.init) – 0xC0000000) {
    *(.initcall1.init)
    *(.initcall2.init)
    *(.initcall3.init)
    *(.initcall4.init)
    *(.initcall5.init)
    *(.initcall6.init)
    *(.initcall7.init)
    }
    __initcall_end = .;

    这就告诉我们.initcall.init节又分成了7个子节,而xxx_initcall入口函数指针具体放在哪一个子节里边儿是由xxx_initcall的定义中,__define_initcall宏的参数决定的,比如core_initcall将函数指针放在.initcall1.init子节,device_initcall将函数指针放在了.initcall6.init子节等等。各个子节的顺序是确定的,即先调用.initcall1.init中的函数指针再调用.initcall2.init中的函数指针,等等。不同的入口函数被放在不同的子节中,因此也就决定了它们的调用顺序。

    do_initcalls()函数

    那些入口函数的调用由do_initcalls函数来完成。

    do_initcall函数通过for循环,由__initcall_start开始,直到__initcall_end结束,依次调用识别到的初始化函数。而位于__initcall_start和__initcall_end之间的区域组成了.initcall.init节,其中保存了由xxx_initcall形式的宏标记的函数地址,do_initcall函数可以很轻松的取得函数地址并执行其指向的函数。

    .initcall.init节所保存的函数地址有一定的优先级,越前面的函数优先级越高,也会比位于后面的函数先被调用。

    由do_initcalls函数调用的函数不应该改变其优先级状态和禁止中断。因此,每个函数执行后,do_initcalls会检查该函数是否做了任何变化,如果有必要,它会校正优先级和中断状态。

    另外,这些被执行的函数有可以完成一些需要异步执行的任务,flush_scheduled_work函数则用于确保do_initcalls函数在返回前等待这些异步任务结束。

    666 static void __init do_initcalls(void)
    667 {
    668   initcall_t *call;
    669   int count = preempt_count();
    670
    671   for (call = __initcall_start; call < __initcall_end; call++) {
    672    ktime_t t0, t1, delta;
    673    char *msg = NULL;
    674    char msgbuf[40];
    675    int result;
    676
    677    if (initcall_debug) {
    678     printk("Calling initcall 0x%p", *call);
    679     print_fn_descriptor_symbol(": %s()",
    680       (unsigned long) *call);
    681     printk("/n");
    682     t0 = ktime_get();
    683    }
    684
    685    result = (*call)();
    686
    687    if (initcall_debug) {
    688     t1 = ktime_get();
    689     delta = ktime_sub(t1, t0);
    690
    691     printk("initcall 0x%p", *call);
    692     print_fn_descriptor_symbol(": %s()",
    693       (unsigned long) *call);
    694     printk(" returned %d./n", result);
    695
    696     printk("initcall 0x%p ran for %Ld msecs: ",
    697      *call, (unsigned long long)delta.tv64 >> 20);
    698     print_fn_descriptor_symbol("%s()/n",
    699      (unsigned long) *call);
    700    }
    701
    702    if (result && result != -ENODEV && initcall_debug) {
    703     sprintf(msgbuf, "error code %d", result);
    704     msg = msgbuf;
    705    }
    706    if (preempt_count() != count) {
    707     msg = "preemption imbalance";
    708     preempt_count() = count;
    709    }
    710    if (irqs_disabled()) {
    711     msg = "disabled interrupts";
    712     local_irq_enable();
    713    }
    714    if (msg) {
    715     printk(KERN_WARNING "initcall at 0x%p", *call);
    716     print_fn_descriptor_symbol(": %s()",
    717       (unsigned long) *call);
    718     printk(": returned with %s/n", msg);
    719    }
    720   }
    721
    722   /* Make sure there is no pending stuff from the initcall sequence */
    723   flush_scheduled_work();
    724 }

  • 相关阅读:
    5 TensorFlow入门笔记之RNN实现手写数字识别
    用python简便地抓取刘昊然的写真(17行代码)
    MFC实现简单飞机大战(含游戏声音)
    4 TensorFlow入门之dropout解决overfitting问题
    3 TensorFlow入门之识别手写数字
    2 TensorFlow入门笔记之建造神经网络并将结果可视化
    1 TensorFlow入门笔记之基础架构
    用python的turtle画分形树
    win10下安装TensorFlow(CPU only)
    python中math常用函数
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8448814.html
Copyright © 2020-2023  润新知