驱动加载流程

观看视频更详细

模块化加载设计,在增加或减少模块时,不需要手动修改主流程,每个模块都有独立的入口,符合开闭原则.

驱动加载中常用的入口函数module_init() 宏如下,从这里分析内核驱动加载顺序,将入口函数分为0-7个优先顺序,并将其编译进init.text 段.主要涉及以下文件。

  • init/main.c
  • System.map
  • include/linux/init.h
  • include/linux/module.h
  • include/asm-generic/vmlinux.lds.h
    1
    2
    3
    4
    5
    6
    int dummy_init(void)
    {
    // ... 省略
    return 0;
    }
    module_init(dummy_init);

    kenrel 启动 init/main.c 中,start_kernel() 调用do_basic_setup()完成init call 调用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    // inlcude/linux/init.h
    typedef int (*initcall_t)(void);
    typedef initcall_t initcall_entry_t;

    //init/main.c
    static initcall_entry_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_basic_setup(void)
    {
    // ... 省略
    do_initcalls();
    }


    static void __init do_initcalls(void)
    {
    // ... 省略

    for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) {
    do_initcall_level(level, command_line);
    }
    // ... 省略
    }

    static void __init do_initcall_level(int level, char *command_line)
    {
    // ... 省略
    initcall_entry_t *fn;
    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
    do_one_initcall(initcall_from_entry(fn));
    }

    int __init_or_module do_one_initcall(initcall_t fn)
    {
    // ...省略

    do_trace_initcall_start(fn);
    ret = fn();
    do_trace_initcall_finish(fn, ret);

    // ... 省略
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    // include/asm-generic/vmlinux.lds.h	

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

    #define INIT_CALLS \
    __initcall_start = .; \
    KEEP(*(.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) \
    __initcall_end = .;

    // arch/arm/kernel/vmlinux.lds.S
    ... 省略
    .init.pv_table : {
    __pv_table_begin = .;
    *(.pv_table)
    __pv_table_end = .;
    }

    INIT_DATA_SECTION(16)

    .exit.data : {
    ARM_EXIT_KEEP(EXIT_DATA)
    }
    ... 省略

    // include/linux/module.h
    #define ___define_initcall(fn, id, __sec) \
    static initcall_t __initcall_##fn##id __used \
    __attribute__((__section__(#__sec ".init"))) = fn;

    #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)

    #define core_initcall(fn) __define_initcall(fn, 1)
    #define postcore_initcall(fn) __define_initcall(fn, 2)
    #define arch_initcall(fn) __define_initcall(fn, 3)

总结

分析System.map 文件,initcall_levels[ ] 数组存放每个等级的入口地址,简化为二维数组更容易理解,代码循环执行每一个入口函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
typedef int (*initcall_t)(void);
typedef initcall_t initcall_entry_t;

static initcall_entry_t *initcall_levels[8][] __initdata = {
{__initcall_ipc_ns_init0 ,__initcall_init_mmap_min_addr0},
{__initcall_vfp_init1 ,__initcall_ptrace_break_init1},
...
省略
...
{__initcall_armv7_pmu_driver_init6 ,__initcall_arch_uprobes_init6},
};

static int do_init_call()
{
initcall_entry_t fn;
for(int level = 0;level < 8;level ++)
{
for (fn = initcall_levels[level][0];fn < initcall_levels[level+1][0];fn++)
{
fn();
}
}
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
# System.map
80b4fbfc D __initcall0_start
80b4fbfc d __initcall_ipc_ns_init0
80b4fc00 d __initcall_init_mmap_min_addr0
80b4fc04 d __initcall_net_ns_init0
...
80b4fc08 D __initcall1_start
80b4fc08 d __initcall_vfp_init1
80b4fc0c d __initcall_ptrace_break_init1
80b4fc10 d __initcall_twd_clk_init1
...
80b4fe88 D __initcall6_start
80b4fe88 d __initcall_armv7_pmu_driver_init6
80b4fe8c d __initcall_arch_uprobes_init6