zoukankan      html  css  js  c++  java
  • linux内核启动分析(3)

    主要分析do_basic_setup函数里面的do_initcalls()函数,这个函数用来调用所有编译内核的驱动模块中的初始化函数。

    static void __init do_initcalls(void)
    {
        initcall_t *call;
        
        for (call = __early_initcall_end; call < __initcall_end; call++)
            do_one_initcall(*call);
    
        /* Make sure there is no pending stuff from the initcall sequence */
        flush_scheduled_work();
    }

    __early_initcall_end 与 __initcall_end之间有

    #define INITCALLS                           
        *(.initcallearly.init)                      
        VMLINUX_SYMBOL(__early_initcall_end) = .;           
        *(.initcall0.init)                      
        *(.initcall0s.init)                     
        *(.initcall1.init)                      
        *(.initcall1s.init)                     
        *(.initcall2.init)                      
        *(.initcall2s.init)                     
        *(.initcall3.init)                      
        *(.initcall3s.init)                     
        *(.initcall4.init)                      
        *(.initcall4s.init)                     
        *(.initcall5.init)                      
        *(.initcall5s.init)                     
        *(.initcallrootfs.init)                     
        *(.initcall6.init)                      
        *(.initcall6s.init)                     
        *(.initcall7.init)                      
        *(.initcall7s.init)

    do_initcalls函数完成对*initcall0-7*的函数的调用工作,遍历所有的模块,当然各个函数的初始化工作有他们自身的函数完成了。

    在include/linux/init.h文件中有引用到".initcall" level ".init"的地方

    /* initcalls are now grouped by functionality into separate
     * subsections. Ordering inside the subsections is determined
     * by link order.
     * For backwards compatibility, initcall() puts the call in
     * the device init subsection.
     *
     * The `id' arg to __define_initcall() is needed so that multiple initcalls
     * can point at the same handler without causing duplicate-symbol build errors.
     */
    
    #define __define_initcall(level,fn,id) 
        static initcall_t __initcall_##fn##id __used 
        __attribute__((__section__(".initcall" level ".init"))) = fn

    然后调用__define_initcall的地方还是在init.h文件中

    /*
     * Early initcalls run before initializing SMP.
     *
     * Only for built-in code, not modules.
     */
    #define early_initcall(fn)      __define_initcall("early",fn,early)
    
    /*
     * A "pure" initcall has no dependencies on anything else, and purely
     * initializes variables that couldn't be statically initialized.
     *
     * This only exists for built-in code, not for modules.
     */
    #define pure_initcall(fn)       __define_initcall("0",fn,0)
    
    #define core_initcall(fn)       __define_initcall("1",fn,1)
    #define core_initcall_sync(fn)      __define_initcall("1s",fn,1s)
    #define postcore_initcall(fn)       __define_initcall("2",fn,2)
    #define postcore_initcall_sync(fn)  __define_initcall("2s",fn,2s)
    #define arch_initcall(fn)       __define_initcall("3",fn,3)
    #define arch_initcall_sync(fn)      __define_initcall("3s",fn,3s)
    #define subsys_initcall(fn)     __define_initcall("4",fn,4)
    #define subsys_initcall_sync(fn)    __define_initcall("4s",fn,4s)
    #define fs_initcall(fn)         __define_initcall("5",fn,5)
    #define fs_initcall_sync(fn)        __define_initcall("5s",fn,5s)
    #define rootfs_initcall(fn)     __define_initcall("rootfs",fn,rootfs)
    #define device_initcall(fn)     __define_initcall("6",fn,6)
    #define device_initcall_sync(fn)    __define_initcall("6s",fn,6s)
    #define late_initcall(fn)       __define_initcall("7",fn,7)
    #define late_initcall_sync(fn)      __define_initcall("7s",fn,7s)
    
    #define __initcall(fn) device_initcall(fn)

    很多子系统比如PCI, USB都会有一个名为subsys_initcall的入口,不过更多的是使用module_init函数。

    module_init宏在MODULE宏有没有定义的情况下展开的内容是不同的,如果这个宏没有定义,基本上表明阁下的模块是要编译进内核的(obj-y)。

    1. 在MODULE没有定义这种情况下,module_init定义如下:#define module_init(x) __initcall(x);

    因为
    #define __initcall(fn)                            device_initcall(fn)
    #define device_initcall(fn) __define_initcall("6",fn,6)
    #define __define_initcall(level,fn,id)
    static initcall_t __initcall_##fn##id __used
    __attribute__((__section__(".initcall" level ".init"))) = fn
    所以,module_init(x)最终展开为:
    static initcall_t __initcall_##fn##id __used
    __attribute__((__section__(".initcall" level ".init"))) = fn
    更直白点,假设阁下driver所对应的模块的初始化函数为int gpio_init(void),那么module_init(gpio_init)实际上等于:
    static initcall_t  __initcall_gpio_init_6 __used __attribute__((__section__(".initcall6.init"))) = gpio_init;
    就是声明一类型为initcall_t(typedef int (*initcall_t)(void))函数指针类型的变量__initcall_gpio_init_6并将gpio_init赋值与它。
    这里的函数指针变量声明比较特殊的地方在于,将这个变量放在了一名为".initcall6.init"节中。接下来结合vmlinux.lds及do_initcalls,那么不难理解阁下模块中的module_init中的初始化函数何时被调用了:在系统启动过程中start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls()。
    如果MODULE这个宏没有定义,基本上表明该模块是要编译进内核的(obj-y)。所以module_exit(x)此时最终会被忽略掉,因为编译进入内核的模块是不需要进行清理工作的。

    2. 在MODULE被定义的情况下(大部分可动态加载的driver模块都属于此, obj-m)

    module_init定义如下:
    #define module_init(initfn)
    static inline initcall_t __inittest(void)
    { return initfn; }
    int init_module(void) __attribute__((alias(#initfn)));
    这段宏定义关键点是后面一句,通过alias将initfn变名为init_module。前面那个__inittest的定义其实是种技巧,用来对initfn进行某种静态的类型检查,如果阁下将模块初始化函数定义成,比如,void gpio_init(void)或者是int gpio_init(int),那么在编译时都会有类似下面的warning:
    GPIO/fsl-gpio.c: In function '__inittest':
    GPIO/fsl-gpio.c:46: warning: return from incompatible pointer type
    通过module_init将模块初始化函数统一别名为init_module,这样以后insmod时候,在系统内部会调用sys_init_module()去找到init_module函数的入口地址。
    如果objdump -t gpio.ko,就会发现init_module和gpio_init位于相同的地址偏移处。简言之,这种情况下模块的初始化函数在insmod时候被调用。

  • 相关阅读:
    c#对XML读取
    WPF--TypeConverter使用
    WPF---对于没有Command属性的添加以下代码可以达到有Command效果
    自定义事件、属性、方法
    读取Excel文件
    ClickOnce安装部署,手动。
    Logger 日志记录
    Maven
    等待与通知范式
    线程状态及基本方法
  • 原文地址:https://www.cnblogs.com/morphling/p/3667219.html
Copyright © 2011-2022 走看看