module_init, fs_initcall

本文详细解析了Linux内核中IPv4初始化的过程,包括如何使用fs_initcall宏注册初始化函数,以及这些函数如何被组织并按顺序执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ipv4内核初始化相关

已有 6249 次阅读 2009-11-4 17:55

所在文件:
net/ipv4/af_inet.c
初始化函数定义:

  1. static int __init inet_init ( void )

初始化函数调用:

  1. fs_initcall ( inet_init ) ; //#define fs_initcall(fn)                 __define_initcall("5",fn,5)

这里的fs_initcall和module_init这样的函数是一样的功能,就是给系统内核添加一个功能函数。

这个宏的定义位于inlcludelinuxinit.h中:

  1. # define __define_initcall ( level , fn , id )
  2. static initcall_t __initcall_ ## fn ## id __used
  3. __attribute__ (( __section__ ( " .initcall "   level " .init " ))) = fn

其中 initcall_t 是个函数指针类型:typedef int (*initcall_t)(void);

而属性 __attribute__((__section__())) 则表示把对象放在一个这个由括号中的名称所指代的section中。
以这个宏定义的的含义是:
1) 声明一个名称为__initcall_##fn##id的函数指针(其中##表示替换连接,);
2) 将这个函数指针初始化为fn;
3) 编译的时候需要把这个函数指针变量放置到名称为 “.initcall” level “.init”的section中(比如level=”1″,代表这个section的名称是 “.initcall1.init”)。

这些衍生宏宏的定义也位于 inlcludelinuxInit.h 中:

  1. # define pure_initcall ( fn )                __define_initcall ( " 0 " , fn , 0 )
  2. # define   core_initcall ( fn )                __define_initcall ( " 1 " , fn , 1 )
  3. # define   core_initcall_sync ( fn )           __define_initcall ( " 1s " , fn , 1 s )
  4. # define   postcore_initcall ( fn )            __define_initcall ( " 2 " , fn , 2 )
  5. # define   postcore_initcall_sync ( fn )       __define_initcall ( " 2s " , fn , 2 s )
  6. # define   arch_initcall ( fn )                __define_initcall ( " 3 " , fn , 3 )
  7. # define   arch_initcall_sync ( fn )           __define_initcall ( " 3s " , fn , 3 s )
  8. # define   subsys_initcall ( fn )              __define_initcall ( " 4 " , fn , 4 )
  9. # define   subsys_initcall_sync ( fn )         __define_initcall ( " 4s " , fn , 4 s )
  10. # define   fs_initcall ( fn )                  __define_initcall ( " 5 " , fn , 5 )
  11. # define   fs_initcall_sync ( fn )             __define_initcall ( " 5s " , fn , 5 s )
  12. # define   rootfs_initcall ( fn )              __define_initcall ( " rootfs " , fn , rootfs )
  13. # define   device_initcall ( fn )              __define_initcall ( " 6 " , fn , 6 )
  14. # define   device_initcall_sync ( fn )         __define_initcall ( " 6s " , fn , 6 s )
  15. # define   late_initcall ( fn )                __define_initcall ( " 7 " , fn , 7 )
  16. # define   late_initcall_sync ( fn )           __define_initcall ( " 7s " , fn , 7 s )

因此通过宏 core_initcall() 来声明的函数指针,将放置到名称为.initcall1.init的section中,而通过宏 postcore_initcall() 来声明的函数指针,将放置到名称为.initcall2.init的section中,依次类推。
在:include/asm-generic/vmlinux.lds.h:

  1. # define INITCALLS                                                       
  2. * ( . initcallearly . init )                                          
  3. VMLINUX_SYMBOL ( __early_initcall_end ) = .;    //注意这里的__early_initcall_end标志
  4. * ( . initcall0 . init )                                              
  5. * ( . initcall0s . init )                                             
  6. * ( . initcall1 . init )                                              
  7. * ( . initcall1s . init )                                             
  8. * ( . initcall2 . init )                                              
  9. * ( . initcall2s . init )                                             
  10. * ( . initcall3 . init )                                              
  11. * ( . initcall3s . init )                                             
  12. * ( . initcall4 . init )                                              
  13. * ( . initcall4s . init )                                             
  14. * ( . initcall5 . init )                                              
  15. * ( . initcall5s . init )                                             
  16. * ( . initcallrootfs . init )                                         
  17. * ( . initcall6 . init )                                              
  18. * ( . initcall6s . init )                                             
  19. * ( . initcall7 . init )                                              
  20. * ( . initcall7s . init )
  21. # define   INIT_CALLS                                                      
  22. VMLINUX_SYMBOL ( __initcall_start ) = .;                  
  23. INITCALLS                                               
  24. VMLINUX_SYMBOL ( __initcall_end ) = .;    //还有这里的__initcall_end

最终跟踪之后这个初始化的段会在arch/x86/kernel/vmlinux.lds.S这样的体系结构中内核二进制文件结构组织的配置文件中。
而在内核Makefile文件中有这样的编译语句:

  1. vmlinux : $ ( vmlinux - lds ) $ ( vmlinux - init ) $ ( vmlinux - main ) vmlinux . o $ ( kallsyms . o ) FORCE
  2. 。。。
  3. vmlinux - lds   := arch /$ ( SRCARCH ) / kernel / vmlinux . lds
  4. 。。。

而在init/main.c 中:

  1. static void __init do_initcalls ( void )
  2. {
  3. initcall_t * call ;
  4.  
  5. for   ( call = __early_initcall_end ; call & lt ; __initcall_end ; call ++ )
  6. do_one_initcall ( * call ) ;
  7.  
  8. /* Make sure there is no pending stuff from the initcall sequence */
  9. flush_scheduled_work () ;
  10. }

该函数的调用关系如下:

  1. start_kernel --& gt ; rest_init -& gt ; kernel_thread ( kernel_init , NULL , CLONE_FS | CLONE_SIGHAND ) ;
  2. |
  3. -& gt ; kernel_init ( void * unused ) -& gt ; do_initcalls ( void )

也就是说对于所有的内核模块或是其它的以类似该形式加入到内核中的程序,都最终在内核所在的二进制文件中是有一个固定的段来存放的,而且内核在初始化的过程中也是找到这些段的地址让后做相应的加载和执行。

<think>好的,我现在需要帮助用户了解Linux内核模块中module_init和late_initcall的用法及区别。首先,我要回忆一下内核初始化的相关知识。记得内核模块的初始化函数是通过宏来定义的,这些宏决定了函数在内核启动过程中的调用顺序。 用户问的是module_init和late_initcall,所以需要明确这两者的不同之处。可能还有其他类似的宏,比如__initcall,但用户主要关注这两个。首先,我应该解释每个宏的基本用法,然后比较它们的执行顺序和用途差异。 我记得内核的初始化分为多个阶段,不同的初始化宏对应不同的优先级。module_init通常用于设备驱动,而late_initcall可能用于那些需要在内核初始化后期执行的模块。需要确认这一点是否正确。例如,module_init对应的初始化级别可能比late_initcall更早,或者更晚? 接下来,我需要查阅内核源码中的相关信息,或者参考内核文档。比如,在include/linux/init.h中,定义了不同级别的初始化宏,如core_initcall、postcore_initcall、arch_initcall、subsys_initcallfs_initcall、device_initcall、late_initcall等,它们的顺序是依次递增的,即优先级依次降低。而module_init宏会根据模块是否内置到内核还是作为可加载模块来区分:如果是内置的,它等同于device_initcall;如果是可加载模块,则会被标记为模块的初始化函数,在模块加载时由insmod或modprobe调用。 所以,当模块被编译进内核时,module_init对应的初始化函数会在device_initcall阶段执行,而late_initcall则是在更晚的阶段执行。因此,它们的执行顺序不同,导致用途也不同。late_initcall通常用于那些需要等待其他子系统初始化完成后再执行的代码。 需要确保这些信息准确无误,可能需要引用相关的内核文档或书籍。例如,《Linux设备驱动程序》一书中可能提到这些初始化宏的使用方法。另外,应该给出示例代码,说明如何在实际模块中使用这两个宏,以及它们在内核启动时的执行顺序差异。 用户可能还想知道为什么会有不同的初始化宏,以及如何选择合适的宏。这时候需要解释内核初始化的阶段划分,以及不同子系统之间的依赖关系。例如,某些驱动可能依赖于其他子系统已经初始化完成,因此需要将它们的初始化函数放在较晚的阶段。 最后,总结两者的主要区别:执行顺序不同,使用场景不同。同时,提醒用户注意模块是内置还是可加载的情况,因为module_init的行为会有所不同。如果是可加载模块,其初始化函数在模块插入时执行,而内置模块则在对应的初始化阶段执行。 在生成相关问题时,需要考虑用户可能进一步的问题,例如其他初始化宏的区别,或者如何调试初始化顺序问题。这样可以帮助用户更深入地理解内核模块的初始化机制。</think>### module_init和late_initcall的区别与用法 #### 一、基本定义 1. **module_init** 是Linux内核模块最常用的初始化宏,用于声明模块的入口函数。根据模块类型(内置/可加载)有不同的行为: - 当模块**编译进内核**时,初始化函数在`device_initcall`阶段执行(第6级) - 当模块**动态加载**时,初始化函数在`insmod/modprobe`时执行 2. **late_initcall** 属于内核初始化阶段中**最后一级**的初始化宏(第7级),专门用于需要延迟执行的初始化操作[^1] #### 二、执行顺序对比 $$ \begin{array}{|c|c|} \hline \text{初始化阶段} & \text{优先级} \\ \hline core\_initcall & 1 \\ postcore\_initcall & 2 \\ arch\_initcall & 3 \\ subsys\_initcall & 4 \\ fs\_initcall & 5 \\ device\_initcall & 6 \\ late\_initcall & 7 \\ \hline \end{array} $$ #### 三、典型使用场景 1. **module_init** 设备驱动初始化(如注册PCI驱动)、子系统注册等需要较早初始化的操作 ```c // 网络驱动示例 static int __init mynet_init(void) { register_netdev(net_device); } module_init(mynet_init); ``` 2. **late_initcall** 需要等待其他子系统就绪的操作,例如: - 依赖文件系统初始化的模块 - 需要最后初始化的硬件组件 - 系统状态监控模块 ```c // 延迟初始化示例 static int __init late_demo(void) { printk("This runs after all regular initcalls"); } late_initcall(late_demo); ``` #### 四、关键差异总结 | 特性 | module_init | late_initcall | |---------------------|--------------------------------|-----------------------| | 执行阶段 | 第6级(内置模块)或动态加载时 | 第7级 | | 模块类型 | 支持动态和静态模块 | 仅静态编译进内核的模块| | 典型延迟 | 10ms级 | 100ms级 | | 依赖关系处理 | 常规依赖 | 强延迟依赖 | #### 五、调试技巧 1. 查看初始化顺序: ```bash dmesg | grep "initcall" ``` 2. 通过`/sys/module/`查看模块初始化状态: ```bash ls /sys/module/<module_name>/initstate ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值