module_init宏分析

本文详细解析了Linux内核中用于模块初始化和退出的module_init和module_exit宏的具体实现,包括宏展开过程、类型定义、段加载及链接脚本配置,以及insmod过程中的地址获取与函数调用。

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

一直在使用module_init和module_exit宏,但是对于这两个宏依然比较陌生,不知道它到底为程序员做了什么东西,今天闲来无事,就打开source insight分析了下它的具体实现,瞬间明白了,现在将笔记整理下。以经典的hello_world模块为例,在模块实现文件中,我们编写了如下语句:

module_init(hello_init);

那么它会被编译器展开成什么样子呢?

在文件linux/init.h中定义了module_init.

#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)

#define device_initcall(fn) __define_initcall("6",fn,6)

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

那么在编译时刻,编译器会将其转化为如下语句
static initcall_t __initcall_hello_init6 __used __attribute__((__section__(".initcall6 .init"))) = hello_init;
实际上定义了一个类型为函数指针的变量__initcall_hello_init6,存放的是hello_init函数地址.
其中initcall_t是一个函数指针 typedef int (*initcall_t)(void); //linux/init.h中定义,
__used也是一个宏,在使用GCC3.4之前的编译器被展开成__attribute__((unused))来禁止编译器弹出有关函数没有被用到的的警告信息。在3.4之后被展开成__attribute__((used))功能一样
__attribute__((__section__(".initcall6 .init")))将该变量加载到段.initcall6.init上,在链接脚本头文件上可以找到如下语句:

#define INITCALLS \
*(.initcallearly.init)\
VMLINUX_SYMBOL(__early_initcall_end) = .;\
  *(.initcall0.init)\
  *(.initcall0s.init)\
  *(.initcall1.init)\
  *(.initcall1s.init)\
  *(.initcall2.init)\
  *(.initcall2s.init)\
  *(.initcall3.init)\
  *(.initcall3s.init)\
  *(.initcall4.init)\
  *(.initcall4s.init)\
  *(.initcall5.init)\
  *(.initcall5s.init)\
*(.initcallrootfs.init)\
  *(.initcall6.init)\
  *(.initcall6s.init)\
  *(.initcall7.init)\
  *(.initcall7s.init)

编译过内核后,生成的vmlinux.lds文件中有如下语句:
  
__initcall_start = .; *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;
当insmod的时候,内核从initcall6.init段中读取到该地址,然后跳转到该地址去执行,所以就打印出hello_init语句中实现的输出.


<think>好的,我需要回答用户关于Linux内核模块开发中module_platform_driver和module_init的区别的问题。首先,我得回忆一下这两个的用途和它们在内核中的作用。 用户之前问过内核模块中局部变量大小的限制,现在转向驱动初始化的,可能需要更深入的驱动开发知识。用户可能是在学习编写设备驱动,遇到了这两个,不清楚何时使用哪一个,或者它们的内部机制有何不同。 首先,module_init是一个常见的,用于指定模块的初始化函数。当模块被加载时(比如通过insmod),这个函数会被调用。而module_platform_driver似乎与平台设备驱动相关,可能用于简化平台驱动的注册流程。 接下来,我需要详细对比这两个的定义和用途。module_init是通用的,适用于任何类型的模块初始化,而module_platform_driver可能特定于平台设备驱动,可能封装了更多的步骤,比如注册platform_driver结构体,自动处理probe和remove函数。 可能需要查看内核源码中的定义。比如,module_platform_driver可能展开后包含了module_initmodule_exit,同时自动注册和注销platform_driver。而使用module_init的话,开发者需要手动调用platform_driver_register和platform_driver_unregister。 另外,需要说明使用module_platform_driver的好处,比如代码更简洁,减少样板代码,避免手动编写初始化和退出函数。而module_init则更灵活,适用于非平台驱动的情况,或者需要自定义初始化流程的场景。 还要注意两者的参数不同。module_init的参数是一个函数指针,而module_platform_driver的参数是一个platform_driver结构体。这样,module_platform_driver可能在内部处理了这个结构体的注册,而module_init需要开发者自己处理。 此外,需要提到编译阶段的影响,比如使用module_platform_driver是否会影响模块的依赖关系或初始化的顺序。可能还要指出在内核文档或源码中的具体位置,方便用户进一步查阅。 可能会有一些例子,比如用module_init时需要自己写init和exit函数,而用module_platform_driver则可以简化代码。例如,使用module_platform_driver时,只需要定义platform_driver结构体,并调用,而无需编写注册和注销的代码。 需要确保回答的结构清晰,先分别解释两者的定义和作用,再对比它们的区别,最后给出使用场景的建议。同时,使用代码示例来说明差异,这样用户更容易理解。 最后,检查是否有遗漏点,比如热插拔支持、设备树匹配等是否与这两个有关联。可能需要提到module_platform_driver更适合与设备树配合使用的平台驱动,而module_init更通用,适用于各种驱动类型。 总结时,强调module_platform_driver是对module_init的封装,专为平台驱动设计,简化代码,而module_init则是基础机制,适用于所有模块初始化。</think>--- ### 核心区别 `module_platform_driver` 和 `module_init` 是 Linux 内核驱动开发中用于模块初始化的两种不同机制,主要差异体现在 **设计层级** 和 **自动化程度** 上。 --- ### 1. `module_init`:基础初始化机制 #### 功能特性 - **通用模块入口**:适用于所有内核模块(驱动、文件系统等) - **手动管理**:需要开发者显式编写注册/注销逻辑 - **灵活性强**:可处理任意类型的初始化操作 #### 典型用法 ```c static int __init my_driver_init(void) { // 手动注册驱动 platform_driver_register(&my_platform_driver); return 0; } static void __exit my_driver_exit(void) { // 手动注销驱动 platform_driver_unregister(&my_platform_driver); } module_init(my_driver_init); module_exit(my_driver_exit); ``` --- ### 2. `module_platform_driver`:平台驱动专用 #### 功能特性 - **平台驱动专用**:专为 `platform_driver` 结构体设计 - **自动封装**:隐式处理注册/注销操作 - **代码简化**:通过展开生成标准初始化代码 #### 展开原理(以 Linux 5.x 内核为例) ```c #define module_platform_driver(__platform_driver) \ module_driver(__platform_driver, platform_driver_register, \ platform_driver_unregister) ``` 等价于自动生成: ```c static int __init my_driver_init(void) { return platform_driver_register(&my_platform_driver); } module_init(my_driver_init); static void __exit my_driver_exit(void) { platform_driver_unregister(&my_platform_driver); } module_exit(my_driver_exit); ``` --- ### 关键对比表 | 特性 | `module_init` | `module_platform_driver` | |---------------------|--------------------------------|------------------------------------| | **适用范围** | 所有内核模块 | 仅限平台驱动(`platform_driver`) | | **代码量** | 需要手动编写注册/注销代码 | 自动生成注册/注销逻辑 | | **错误处理** | 可自定义错误处理流程 | 使用标准错误返回机制 | | **维护性** | 需手动维护初始化顺序 | 自动保证初始化的正确性 | | **典型使用场景** | 非平台驱动、复杂初始化需求 | 标准平台驱动快速开发 | --- ### 使用建议 1. **优先使用 `module_platform_driver`** 当满足以下条件时: - 驱动类型为平台设备驱动 - 不需要特殊的初始化顺序控制 - 无需在注册前后执行额外操作 2. **选择 `module_init`** 当需要: ```c // 示例:需要初始化前配置硬件 static int __init custom_init(void) { hw_preconfigure(); int ret = platform_driver_register(&drv); if(ret) { hw_rollback(); return ret; } init_debugfs(); // 附加初始化 return 0; } ``` --- ### 底层实现分析 #### `module_platform_driver` 展开流程 ```c module_platform_driver(my_driver) ↓ 展开为 module_driver(my_driver, platform_driver_register, platform_driver_unregister) ↓ 进一步展开 static int __init my_driver_init(void) { \ return platform_driver_register(&my_driver); \ } \ module_init(my_driver_init); \ static void __exit my_driver_exit(void) { \ platform_driver_unregister(&my_driver); \ } \ module_exit(my_driver_exit); ``` #### 初始化顺序控制 - 通过 `module_init` 的 `__initcall` 层级: ```c #define module_init(x) __initcall(x, 6) ``` 内核启动时按优先级顺序执行初始化函数(1-7级,6为设备驱动级) --- ### 调试技巧 1. 查看初始化顺序: ```bash # 显示所有 __initcall 函数 cat /proc/kallsyms | grep __initcall ``` 2. 动态调试驱动加载: ```bash # 加载时显示初始化函数地址 insmod my_driver.ko dyndbg=+p ``` --- ### 兼容性注意事项 | 内核版本 | `module_platform_driver` 可用性 | |------------------|--------------------------------| | < Linux 2.6.31 | 不可用 | | ≥ Linux 2.6.31 | 可用(推荐使用) | --- 通过合理选择这两种机制,开发者可以在代码简洁性和控制灵活性之间取得最佳平衡。对于标准平台驱动开发,`module_platform_driver` 能显著减少样板代码量并降低错误概率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值