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時候被調用。

相關文章
相關標籤/搜索