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);
  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值