• 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
    • 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
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    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; 
    
    • 1
    • 2
    • 3

    通过__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     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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
    
    • 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

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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 }
    
    • 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
    • 53
    • 54
    • 55
    • 56
    • 57

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    【VUE复习·6】监视属性watch:用途、两种写法、简写、应用时注意事项(重点)、深度监视(重点)
    P06 DDL
    读书笔记:c++对话系列,模板方法模式(Template Method Pattern)
    树状数组——一个简单的整数问题
    【Docker项目实战】使用Docker部署HFish蜜罐系统
    会自动清除的文件——tempfile
    宇视的会议平板使用操作指导
    【打卡】【Linux 设备管理机制】21天学习挑战赛—RK3399平台开发入门到精通-Day17
    C/C++面试题分享「虚函数、多态、内存管理与软件调试篇」
    【Python基础】Python十进制二进制八进制十六进制 ascii转换
  • 原文地址:https://blog.csdn.net/u014100559/article/details/126376616