linux内核启动分析(3)

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

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之间有ide

#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"的地方post

/* 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文件中ui

/*
 * 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函数。spa

module_init宏在MODULE宏有没有定义的状况下展开的内容是不一样的,若是这个宏没有定义,基本上代表阁下的模块是要编译进内核的(obj-y)。指针

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

由于
#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)此时最终会被忽略掉,由于编译进入内核的模块是不须要进行清理工做的。code

 

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

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时候被调用。

相关文章
相关标签/搜索