linux驱动程序的加载顺序
该文档的分析是基于linux-4.14去做的分析。
1 linux内核驱动加载宏
Linux内核为不同驱动的加载顺序对应不同的优先级,定义了一些宏:
这些宏位于include/linux/init.h
文件中,最终驱动通过使用下面的这些不同的加载宏被放入到不同的段(.section
)中,在内核初始化的时候,会调用do_initcalls
去处理这些段。
1.1 linux内核驱动加载宏
149 /*
150 * initcalls are now grouped by functionality into separate
151 * subsections. Ordering inside the subsections is determined
152 * by link order.
153 * For backwards compatibility, initcall() puts the call in
154 * the device init subsection.
155 *
156 * The `id' arg to __define_initcall() is needed so that multiple initcalls
157 * can point at the same handler without causing duplicate-symbol build errors.
158 *
159 * Initcalls are run by placing pointers in initcall sections that the
160 * kernel iterates at runtime. The linker can do dead code / data elimination
161 * and remove that completely, so the initcall sections have to be marked
162 * as KEEP() in the linker script.
163 */
164
165 #define __define_initcall(fn, id) \
166 static initcall_t __initcall_##fn##id __used \
167 __attribute__((__section__(".initcall" #id ".init"))) = fn;
168
169 /*
170 * Early initcalls run before initializing SMP.
171 *
172 * Only for built-in code, not modules.
173 */
174 #define early_initcall(fn) __define_initcall(fn, early)
175
176 /*
177 * A "pure" initcall has no dependencies on anything else, and purely
178 * initializes variables that couldn't be statically initialized.
179 *
180 * This only exists for built-in code, not for modules.
181 * Keep main.c:initcall_level_names[] in sync.
182 */
183 #define pure_initcall(fn) __define_initcall(fn, 0)
184
185 #define core_initcall(fn) __define_initcall(fn, 1)
186 #define core_initcall_sync(fn) __define_initcall(fn, 1s)
187 #define postcore_initcall(fn) __define_initcall(fn, 2)
188 #define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
189 #define arch_initcall(fn) __define_initcall(fn, 3)
190 #define arch_initcall_sync(fn) __define_initcall(fn, 3s)
191 #define subsys_initcall(fn) __define_initcall(fn, 4)
192 #define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
193 #define fs_initcall(fn) __define_initcall(fn, 5)
194 #define fs_initcall_sync(fn) __define_initcall(fn, 5s)
195 #define rootfs_initcall(fn) __define_initcall(fn, rootfs)
196 #define device_initcall(fn) __define_initcall(fn, 6)
197 #define device_initcall_sync(fn) __define_initcall(fn, 6s)
198 #define late_initcall(fn) __define_initcall(fn, 7)
199 #define late_initcall_sync(fn) __define_initcall(fn, 7s)
200
201 #define __initcall(fn) device_initcall(fn)
202
203 #define __exitcall(fn) \
204 static exitcall_t __exitcall_##fn __exit_call = fn
205
206 #define console_initcall(fn) \
207 static initcall_t __initcall_##fn \
208 __used __section(.con_initcall.init) = fn
209
210 #define security_initcall(fn) \
211 static initcall_t __initcall_##fn \
212 __used __section(.security_initcall.init) = fn
1.2 __define_initcall
下面重点关注一下__define_initcall
的实现:
165 #define __define_initcall(fn, id) \
166 static initcall_t __initcall_##fn##id __used \
167 __attribute__((__section__(".initcall" #id ".init"))) = fn;
通过__define_initcall(fn, id)
宏,依据id将不同的fn放置于不同的段(.section
)中。
__attribute__
是用于设置函数属性、变量属性以及类型属性;__attribute__((section("section_name")))
其作用是将特定的函数或数据放入指定名为"section_name"
的段(.section
)中;“#”
的作用是将后面紧跟着的id转换为字符串;“##”
则用于连接两个不同的字符。__attribute__((_used_))
的作用是告诉编译器这个静态符号在编译的时候即使没有使用到也要保留这个符号;__attribute__((__section__(".initcall" #id ".init"))) = fn
的作用即为将fn放入定义好的initcalln.init
的段中,n代表的是id。
2 链接文件(.ld
)中的定义
对于linux内核,链接器所需要处理的链接文件位于arch/arm/kernel/vmlinux.lds.S
文件,其对应各段的定义位于include/asm-generic/vmlinux.lds.h
文件。
arch/arm/kernel/vmlinux.lds.S
文件的.init.data
段标识了要将哪些段放入到.init.data
段中。我们需要重点关注的是INIT_CALLS
段。
✗ 218 .init.data : {
✗ 219 INIT_DATA
✗ 220 INIT_SETUP(16)
✗ 221 INIT_CALLS
✗ 222 CON_INITCALL
✗ 223 SECURITY_INITCALL
✗ 224 INIT_RAM_FS
✗ 225 }
✗ 226 .exit.data : {
✗ 227 ARM_EXIT_KEEP(EXIT_DATA)
✗ 228 }
INIT_CALLS
的定义位于include/asm-generic/vmlinux.lds.h
文件中,如下代码所示,INIT_CALL
段也包含很多不同的段,现在拿INIT_CALLS_LEVEL
为例来说明,INIT_CALLS_LEVEL
段定义为__initcall##level##_start
,将.initcall##level##.init
以及.initcall##level##s.init
放入到INIT_CALLS
段中。
level对应于在1.2 __define_initcall定义的id
734 #define INIT_SETUP(initsetup_align) \
735 . = ALIGN(initsetup_align); \
736 VMLINUX_SYMBOL(__setup_start) = .; \
737 KEEP(*(.init.setup)) \
738 VMLINUX_SYMBOL(__setup_end) = .;
739
740 #define INIT_CALLS_LEVEL(level) \
741 VMLINUX_SYMBOL(__initcall##level##_start) = .; \
742 KEEP(*(.initcall##level##.init)) \
743 KEEP(*(.initcall##level##s.init)) \
744
745 #define INIT_CALLS \
746 VMLINUX_SYMBOL(__initcall_start) = .; \
747 KEEP(*(.initcallearly.init)) \
748 INIT_CALLS_LEVEL(0) \
749 INIT_CALLS_LEVEL(1) \
750 INIT_CALLS_LEVEL(2) \
751 INIT_CALLS_LEVEL(3) \
752 INIT_CALLS_LEVEL(4) \
753 INIT_CALLS_LEVEL(5) \
754 INIT_CALLS_LEVEL(rootfs) \
755 INIT_CALLS_LEVEL(6) \
756 INIT_CALLS_LEVEL(7) \
757 VMLINUX_SYMBOL(__initcall_end) = .;
758
759 #define CON_INITCALL \
760 VMLINUX_SYMBOL(__con_initcall_start) = .; \
761 KEEP(*(.con_initcall.init)) \
762 VMLINUX_SYMBOL(__con_initcall_end) = .;
763
764 #define SECURITY_INITCALL \
765 VMLINUX_SYMBOL(__security_initcall_start) = .; \
766 KEEP(*(.security_initcall.init)) \
767 VMLINUX_SYMBOL(__security_initcall_end) = .;
768
769 #ifdef CONFIG_BLK_DEV_INITRD
770 #define INIT_RAM_FS \
771 . = ALIGN(4); \
772 VMLINUX_SYMBOL(__initramfs_start) = .; \
773 KEEP(*(.init.ramfs)) \
774 . = ALIGN(8); \
775 KEEP(*(.init.ramfs.info))
776 #else
777 #define INIT_RAM_FS
778 #endif
3 linux内核对initcall的处理流程
下面将分析从start_kernel开始一直到do_initcalls的调用关系。对驱动各模块的加载处理也是在do_initcalls中处理的。
3.1 调用关系
所哟的这些函数均位于init/main.c文件中
| - start_kernel ---(init/main.c)
| - rest_init ---(init/main.c)
| - kernel_init (pid = kernel_thread(kernel_init, NULL, CLONE_FS)) ---(init/main.c)
| - kernel_init_freeable ---(init/main.c)
| - do_basic_setup ---(init/main.c)
| - do_initcalls ---(init/main.c)
| - do_initcall_level
| - do_one_initcall
3.2 do_initcalls
如下所示,do_initcalls调用do_initcall_level进而再调用do_one_initcall去做更具体的处理。initcall_levels 数组中的各个成员均和链接文件(.ld)中的定义是一致的。所有的这些均包含在INITCALLS段中。
845 extern initcall_t __initcall_start[];
846 extern initcall_t __initcall0_start[];
847 extern initcall_t __initcall1_start[];
848 extern initcall_t __initcall2_start[];
849 extern initcall_t __initcall3_start[];
850 extern initcall_t __initcall4_start[];
851 extern initcall_t __initcall5_start[];
852 extern initcall_t __initcall6_start[];
853 extern initcall_t __initcall7_start[];
854 extern initcall_t __initcall_end[];
855
856 static initcall_t *initcall_levels[] __initdata = {
857 __initcall0_start,
858 __initcall1_start,
859 __initcall2_start,
860 __initcall3_start,
861 __initcall4_start,
862 __initcall5_start,
863 __initcall6_start,
864 __initcall7_start,
865 __initcall_end,
866 };
867
868 /* Keep these in sync with initcalls in include/linux/init.h */
869 static char *initcall_level_names[] __initdata = {
870 "early",
871 "core",
872 "postcore",
873 "arch",
874 "subsys",
875 "fs",
876 "device",
877 "late",
878 };
879
880 static void __init do_initcall_level(int level)
881 {
882 initcall_t *fn;
883
884 strcpy(initcall_command_line, saved_command_line);
885 parse_args(initcall_level_names[level],
886 initcall_command_line, __start___param,
887 __stop___param - __start___param,
888 level, level,
889 NULL, &repair_env_string);
890
891 for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
892 do_one_initcall(*fn);
893 }
894
895 static void __init do_initcalls(void)
896 {
897 int level;
898
899 for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
900 do_initcall_level(level);
901 }
4 具体样例分析
4.1 module_init分析
module_init的宏定义位于drivers/staging/lustre/lustre/include/lustre_compat.h文件中,而module_init会调用到late_initcall宏定义。而late_initcall宏定义参考1.1 linux内核驱动加载宏章的声明,late_initcall的
51 /*
52 * OBD need working random driver, thus all our
53 * initialization routines must be called after device
54 * driver initialization
55 */
56 #ifndef MODULE
57 #undef module_init
58 #define module_init(a) late_initcall(a)
59 #endif
4.2 builtin_platform_driver分析
builtin_platform_driver的宏定义位于include/linux/platform_device.h文件中,而builtin_platform_driver会调用到builtin_driver,
231 /* builtin_platform_driver() - Helper macro for builtin drivers that
232 * don't do anything special in driver init. This eliminates some
233 * boilerplate. Each driver may only use this macro once, and
234 * calling it replaces device_initcall(). Note this is meant to be
235 * a parallel of module_platform_driver() above, but w/o _exit stuff.
236 */
237 #define builtin_platform_driver(__platform_driver) \
238 builtin_driver(__platform_driver, platform_driver_register)
builtin_driver宏定义位于include/linux/device.h
文件中,该宏定义会调用到device_initcall
宏,而该宏的定义依然是需要参考1.1 linux内核驱动加载宏的声明部分。
1511 /**
1512 * builtin_driver() - Helper macro for drivers that don't do anything
1513 * special in init and have no exit. This eliminates some boilerplate.
1514 * Each driver may only use this macro once, and calling it replaces
1515 * device_initcall (or in some cases, the legacy __initcall). This is
1516 * meant to be a direct parallel of module_driver() above but without
1517 * the __exit stuff that is not used for builtin cases.
1518 *
1519 * @__driver: driver name
1520 * @__register: register function for this driver type
1521 * @...: Additional arguments to be passed to __register
1522 *
1523 * Use this macro to construct bus specific macros for registering
1524 * drivers, and do not use it on its own.
1525 */
1526 #define builtin_driver(__driver, __register, ...) \
1527 static int __init __driver##_init(void) \
1528 { \
1529 return __register(&(__driver) , ##__VA_ARGS__); \
1530 } \
1531 device_initcall(__driver##_init);