LINUX內核中的xx_initcall初始化標號

From: csdn 800th       http://blog.csdn.net/thl789/article/details/6581146  


      LINUX內核中有很多的初始化指示標誌postcore_initcall(), arch_initcall(), subsys_initcall(), device_initcall(), etc. 這些起什麼作用呢?查閱源代碼(android4.1-kernel3.4)並搜索網上相關文章,對此做一總結。

  1. 初始化標號

先看這些宏的定義(定義在文件include/linux/init.h中)

/* 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

/*
 * 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)

#define __exitcall(fn) \
    static exitcall_t __exitcall_##fn __exit_call = fn

#define console_initcall(fn) \
    static initcall_t __initcall_##fn \
    __used __section(.con_initcall.init) = fn

#define security_initcall(fn) \
    static initcall_t __initcall_##fn \
    __used __section(.security_initcall.init) = fn

struct obs_kernel_param {
    const char *str;
    int (*setup_func)(char *);
    int early;
};

  1. __define_initcall

這些宏都用到了__define_initcall(),再看看它的定義(同樣定義在文件include/linux/init.h中)

#define __define_initcall(level,fn,id) \
    static initcall_t __initcall_##fn##id __used \
    __attribute__((__section__(".initcall" level ".init"))) = fn

這其中initcall_t是函數指針,原型如下,
    typedef int (*initcall_t)(void);  

而屬性 __attribute__((__section__())) 則表示把對象放在一個這個由括號中的名稱所指代的section中。

所以__define_initcall的含義是:

1) 聲明一個名稱爲__initcall_##fn##id的函數指針;

2) 將這個函數指針初始化爲fn;

3) 編譯的時候需要把這個函數指針變量放置到名稱爲 ".initcall" level ".init"的section中。


3.  放置.initcall.init SECTION

明確了__define_initcall的含義,就知道了是分別將這些初始化標號修飾的函數指針放到各自的section中的。

SECTION“.initcall”level”.init”被放入 INIT_CALLS中(include/asm-generic/vmlinux.lds.h)

     #define INIT_CALLS_LEVEL(level)                        \
        VMLINUX_SYMBOL(__initcall##level##_start) = .;        \
        *(.initcall##level##.init)                \
        *(.initcall##level##s.init)                \

#define INIT_CALLS                            \
        VMLINUX_SYMBOL(__initcall_start) = .;            \
        *(.initcallearly.init)                    \
        INIT_CALLS_LEVEL(0)                    \
        INIT_CALLS_LEVEL(1)                    \
        INIT_CALLS_LEVEL(2)                    \
        INIT_CALLS_LEVEL(3)                    \
        INIT_CALLS_LEVEL(4)                    \
        INIT_CALLS_LEVEL(5)                    \
        INIT_CALLS_LEVEL(rootfs)                \
        INIT_CALLS_LEVEL(6)                    \
        INIT_CALLS_LEVEL(7)                    \
        VMLINUX_SYMBOL(__initcall_end) = .;


INIT_CALLS中定義的SECTION都是在arch/xxx/kernel/vmlinux.lds.S中放在.init段的。

     .init.data : {
      ......
        INIT_CALLS
      ......
    }

4.   初始化.initcallxx.init裏的函數

而這些SECTION裏的函數在初始化時被順序執行(init內核線程->do_basic_setup()[main.c#778]->do_initcalls())。

程序(init/main.c文件do_initcalls()函數)如下,do_initcalls()把.initcallXX.init中的函數按順序都執行一遍。

extern initcall_t __initcall_start[];
extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[];
extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[];
extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[];
extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[];
extern initcall_t __initcall_end[];

static initcall_t *initcall_levels[] __initdata = {
    __initcall0_start,
    __initcall1_start,
    __initcall2_start,
    __initcall3_start,
    __initcall4_start,
    __initcall5_start,
    __initcall6_start,
    __initcall7_start,
    __initcall_end,
};

static void __init do_initcalls(void)
{
    int level;

    for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
        do_initcall_level(level);
}

static void __init do_initcall_level(int level)
{
    extern const struct kernel_param __start___param[], __stop___param[];
    initcall_t *fn;

    strcpy(static_command_line, saved_command_line);
    parse_args(initcall_level_names[level],
           static_command_line, __start___param,
           __stop___param - __start___param,
           level, level,
           repair_env_string);

    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
        do_one_initcall(*fn);
}

*******Done*******
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章