module_init与module_exit的分析

        在编写驱动模块的时候有两个函数经常被使用也必须被使用,分别是module_init和module_exit,这两个函数分别在加载和卸载驱动时被调用,即调用insmod和rmmod命令的时候,但是insmod和rmmod不能识别这两个函数,它只能识别init_module和cleanup_module其实init_module和cleanup_module相当于是module_init和module_exit的别名,下面会详细讲解

我们知道驱动可以作为一个模块动态的加载到内核里,也可以作为内核的一部分静态的编译进内核,module_init/module_exit也就有了两个含义:

一、编译成模块

/* Each module must use one module_init(). */
#define module_init(initfn) \
    static inline initcall_t __inittest(void) \
        { return initfn; } \
    int init_module(void) __attribute__((alias(#initfn)));

/* This is only required if you want to be unloadable. */
#define module_exit(exitfn) \
    static inline exitcall_t __exittest(void) \
        { return exitfn; } \
    void cleanup_module(void) __attribute__((alias(#exitfn)));

module_init有两个含义:

  1. 验证函数的格式

         initcall_t是一个函数指针。定义为:

typedef int (*initcall_t)(void);

         static inline initcall_t __inittest(void) { return initfn; }

         这个函数的作用是定义一个函数__inittest返回值类型为initcall_t如果我们传进来的函数返回值类型不是initcall_t这个类型则会报错,即验证我们定义的函数是否符合规范

         所以我们写加载函数的时候必须是返回值为int参数为void的函数。

2、定义别名

int init_module(void) __attribute__((alias(#initfn)));

         这段代码的作用是给我们的加载函数定义一个别名init_module因为insmod命令只能识别函数init_module

         module_exit的作用和module_init一样,同样也是验证函数格式和定义别名。

         例:

static int __init acm_ms_init(void)
{
    return usb_composite_probe(&acm_ms_driver, acm_ms_bind);
}
module_init(acm_ms_init);
展开后为:
static inline initcall_t __inittest(void)
{
    return acm_ms_init;//验证acm_ms_init是否和initcall_t为同一类型
}
int init_module(void) __attribute__((alias(#acm_ms_init)));//定义别名init_module

二、静态编译         

         在静态编译的时候module_init的定义如下:

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

#define device_initcall(fn) __define_initcall("6",fn,6)
#define __initcall(fn) device_initcall(fn)
#define module_init(x) __initcall(x);

         关于静态编译的主要函数__define_initcall的分析请参考博客:https://blog.youkuaiyun.com/qq_37600027/article/details/90739265

         module_exit在静态编译的时候没有意义,因为静态编译的驱动无法卸载!

         例:

static int __init acm_ms_init(void)
{
    return usb_composite_probe(&acm_ms_driver, acm_ms_bind);
}
module_init(acm_ms_init);

         展开后,其实就是如下内容

static initcall_t __initcall_acm_ms_init6 __used \
    __attribute__((__section__(".initcall6.init"))) = acm_ms_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时需要自己写initexit函数,而用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、付费专栏及课程。

余额充值