Linux内核如何确定__init调用的顺序?

时间:2012-05-10 18:22:35

标签: linux linux-kernel kernel-module

在驱动程序module_init和内核的其他函数中,内核中有许多__init个调用实例。我怀疑的是内核如何确定__init调用的顺序。更重要的是,它如何确定驱动程序module_init调用的顺序?

2 个答案:

答案 0 :(得分:11)

所有init魔法都在文件中实现:

  1. include/asm-generic/vmlinux.lds.h
  2. include/linux/init.h
  3. init/main.c

  4. 首先,查看包含followinginclude/asm-generic/vmlinux.lds.h

     13  *      . = START;
     14  *      __init_begin = .;
     15  *      HEAD_TEXT_SECTION
     16  *      INIT_TEXT_SECTION(PAGE_SIZE)
     17  *      INIT_DATA_SECTION(...)
     18  *      PERCPU_SECTION(CACHELINE_SIZE)
     19  *      __init_end = .;
    

    INIT_TEXT_SECTIONINIT_DATA_SECTION定义如下:

    790 #define INIT_TEXT_SECTION(inittext_align)                               \
    791         . = ALIGN(inittext_align);                                      \
    792         .init.text : AT(ADDR(.init.text) - LOAD_OFFSET) {               \
    793                 VMLINUX_SYMBOL(_sinittext) = .;                         \
    794                 INIT_TEXT                                               \
    795                 VMLINUX_SYMBOL(_einittext) = .;                         \
    796         }
    797 
    798 #define INIT_DATA_SECTION(initsetup_align)                              \
    799         .init.data : AT(ADDR(.init.data) - LOAD_OFFSET) {               \
    800                 INIT_DATA                                               \
    801                 INIT_SETUP(initsetup_align)                             \
    802                 INIT_CALLS                                              \
    803                 CON_INITCALL                                            \
    804                 SECURITY_INITCALL                                       \
    805                 INIT_RAM_FS                                             \
    806         }
    

    让我们看一下INIT_CALLS定义例如:

    628 #define INIT_CALLS_LEVEL(level)                                         \
    629                 VMLINUX_SYMBOL(__initcall##level##_start) = .;          \
    630                 *(.initcall##level##.init)                              \
    631                 *(.initcall##level##s.init)  
    
    633 #define INIT_CALLS                                                      \
    634                 VMLINUX_SYMBOL(__initcall_start) = .;                   \
    635                 *(.initcallearly.init)                                  \
    636                 INIT_CALLS_LEVEL(0)                                     \
    637                 INIT_CALLS_LEVEL(1)                                     \
    638                 INIT_CALLS_LEVEL(2)                                     \
    639                 INIT_CALLS_LEVEL(3)                                     \
    640                 INIT_CALLS_LEVEL(4)                                     \
    641                 INIT_CALLS_LEVEL(5)                                     \
    642                 INIT_CALLS_LEVEL(rootfs)                                \
    643                 INIT_CALLS_LEVEL(6)                                     \
    644                 INIT_CALLS_LEVEL(7)                                     \
    645                 VMLINUX_SYMBOL(__initcall_end) = .;
    

    您可以看到这定义了标有.initcall...的部分名称。并且所有标记的数据都会进入__initcall_start .. __initcall_end范围。


    现在让我们看看包含following[include/linux/init.h

    44 #define __init          __section(.init.text) __cold notrace
    45 #define __initdata      __section(.init.data)
    

    进一步说:

    189 #define __define_initcall(level,fn,id) \
    190         static initcall_t __initcall_##fn##id __used \
    191         __attribute__((__section__(".initcall" level ".init"))) = fn
    ...
    220 #define device_initcall(fn) __define_initcall("6",fn,6)
    ...
    225 #define __initcall(fn) device_initcall(fn)
    ...
    271 /**
    272  * module_init() - driver initialization entry point
    273  * @x: function to be run at kernel boot time or module insertion
    274  * 
    275  * module_init() will either be called during do_initcalls() (if
    276  * builtin) or at module insertion time (if a module).  There can only
    277  * be one per module.
    278  */
    279 #define module_init(x)  __initcall(x);
    

    因此,您可以看到定义为module_init的{​​{1}}定义为__initcall定义为device_initcall。这里的六个意味着initcall级别。见下文......


    __define_initcall("6",fn,6)包含following

    init/main.c

    正如您所看到的,711 extern initcall_t __initcall_start[]; 712 extern initcall_t __initcall0_start[]; 713 extern initcall_t __initcall1_start[]; 714 extern initcall_t __initcall2_start[]; 715 extern initcall_t __initcall3_start[]; 716 extern initcall_t __initcall4_start[]; 717 extern initcall_t __initcall5_start[]; 718 extern initcall_t __initcall6_start[]; 719 extern initcall_t __initcall7_start[]; 720 extern initcall_t __initcall_end[]; 721 722 static initcall_t *initcall_levels[] __initdata = { 723 __initcall0_start, 724 __initcall1_start, 725 __initcall2_start, 726 __initcall3_start, 727 __initcall4_start, 728 __initcall5_start, 729 __initcall6_start, 730 __initcall7_start, 731 __initcall_end, 732 }; 733 734 /* Keep these in sync with initcalls in include/linux/init.h */ 735 static char *initcall_level_names[] __initdata = { 736 "early", 737 "core", 738 "postcore", 739 "arch", 740 "subsys", 741 "fs", 742 "device", 743 "late", 744 }; 745 746 static void __init do_initcall_level(int level) 747 { 748 extern const struct kernel_param __start___param[], __stop___param[]; 749 initcall_t *fn; 750 751 strcpy(static_command_line, saved_command_line); 752 parse_args(initcall_level_names[level], 753 static_command_line, __start___param, 754 __stop___param - __start___param, 755 level, level, 756 &repair_env_string); 757 758 for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) 759 do_one_initcall(*fn); 760 } 761 762 static void __init do_initcalls(void) 763 { 764 int level; 765 766 for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) 767 do_initcall_level(level); 768 } 只是遍历所有的initcall级别,并为每个级别的条目调用do_one_initcall的每个级别调用do_initcall


    我们还要注意内核在执行后会丢弃所有do_initcall_level函数。所以在内核加载后它们不会在内存中发生。

    就是这样。

答案 1 :(得分:0)

@Ilya 的反响很好。我将添加三个项目:

  1. __initcall 条目可以在 System.map__initcall_start 之间的 __initcall_end 中看到。每个级别也有一个 __initcall[0-7]_start。下面是 futex.c 的初始化示例:core_initcall(futex_init) 和 System.map 条目:ffffffff82abf83c t __initcall_futex_init1

  2. 内置模块不需要明确的 LEVEL_initcall 源定义。它们在链接期间会自动添加到符号表中。请参阅内置模块的 .config。请参阅 CONFIG_SERIAL_8250=yserial/8250 代码和 System.map 条目:ffffffff82ac0020 t __initcall_serial8250_init6

  3. 不存在设备的 __initcall 被调用,但将失败并显示 ENODEV 状态。 do_one_initcall 函数不检查状态。如果这是一个关键子系统,它负责通过恐慌停止启动过程。