驱动之module_init/module_exit

本文解析了Linux内核中模块动态加载与卸载的原理,重点介绍了module_init和module_exit宏的使用方法及其在动态编译和静态编译中的不同作用。

在前面helloworld的编写里面,我们使用了两个宏分别是module_init和module_exit,这里分析下为什么使用这两个宏。

在写模块的时候有两个特殊的函数,分别是init_module和cleanup_module,这两个函数分别在insmod的时候和rmmod的时候调用,并且insmod和rmmod只识别这两个特殊的函数,可是我们前面的例子里面并没有这两个函数。怎么会这样呢,那就必须得说说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、验证加载函数的格式

static inline initcall_t __inittest(void) \

{ return initfn; }

这个函数的作用是验证我们穿过来的加载函数格式是否正确,linux内核规定加载函数的的原型是:

typedef int (*initcall_t)(void);

所以我们写加载函数的时候必须是返回值为int参数为void的函数,这个在内核里要求比较严格,所以我们写加载函数的时候必须按照这个约定。

2、定义别名

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

这段代码的作用是给我们的加载函数定义一个别名,别名就是我们前面提到的init_module,这样insmod就能够执行我们的加载函数了。

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

二、静态编译

在静态编译的时候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

通过这些段代码,我们能够看出最终的结果是将我们的使用module_init修饰的函数指针链接到一个叫.initcall的段里,也就是说最终所以的使用module_init修饰的函数指针都被链接在这个段里,最终内核在启动的时候顺序调用所有链接在这个段里的函数,实现设备的初始化。

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

<think> 首先,用户的问题是:“我想查询使用module_platform_register的驱动是否可以不需要module_init。请问使用module_platform_register的驱动是否可以不使用module_init?” 我需要回顾引用内容来回答这个问题。引用中讨论了module_platform_driver宏和platform_driver_register函数。 从引用[1]:"module_platform_driver会使用module_driver来填充platform_driver_register、platform_driver_unregister。module_platform_driver宏展开就和module_init()、module_exit()是相同的,最终调用platform_driver_register()注册驱动。" 引用[2]:"module_platform_driver是module_init的一种封装,#define module_platform_driver(__platform_driver) \ module_driver(__platform_driver, platform_driver_register, platform_driver_unregister)",并且显示了宏的定义。 引用[3]:比较了两种方法:方法一使用platform_driver_register和platform_driver_unregister手动注册,方法二提到module_platform_driver。 引用[4]:"module_platform_driver宏自动地注册和注销了Platform驱动,而platform_driver_register和platform_driver_unregister函数需要手动调用。" 用户提到的"module_platform_register",但引用中只有"module_platform_driver"。在引用中,没有"module_platform_register",可能是个笔误,用户可能意指"module_platform_driver"。在引用[1]、[2]、[3]、[4]中都提到"module_platform_driver",所以我假设用户说的是"module_platform_driver"。 核心问题是:使用module_platform_driver宏的驱动是否可以不需要显式使用module_init()。 从引用[2]的宏定义: #define module_platform_driver(__platform_driver) \ module_driver(__platform_driver, platform_driver_register, \ platform_driver_unregister) 然后,module_driver宏定义为: #define module_driver(__driver, __register, __unregister, ...) \ static int __init __driver##_init(void) \ { \ return __register(&(__driver) , ##__VA_ARGS__); \ } \ module_init(__driver##_init); \ static void __exit __driver##_exit(void) \ { \ __unregister(&(__driver) , ##__VA_ARGS__); \ } \ module_exit(__driver##_exit); 所以,当使用module_platform_driver宏时,它会自动生成一个__init函数和一个__exit函数,并使用module_initmodule_exit来注册它们。 具体来说,对于驱动my_driver: module_platform_driver(my_driver) 会扩展到: 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_platform_driver宏已经包含了module_initmodule_exit的使用。它封装了驱动的注册和注销过程,所以你不需要手动写module_initmodule_exit;宏自动处理了这一点。 所以,回答是:是的,使用module_platform_driver的驱动不需要显式使用module_init,因为宏内部已经包含了module_init。 在引用[3]中,方法一显示手动使用module_init调用platform_driver_register,而方法二暗示使用module_platform_driver作为替代。 引用[4]说:"module_platform_driver宏自动地注册和注销了Platform驱动",意思是它自动处理了注册。 因此,对于一个使用module_platform_driver的驱动,你不需要额外的module_init调用;宏本身定义了模块的入口和出口点。 总结回答:使用module_platform_driver宏的驱动不需要显式使用module_init,因为该宏内部定义了module_init函数。 现在,回答要用中文,结构清晰。 最后,添加相关问题,如系统指令所示。 相关问题应该基于这个主题生成。 系统指令说:"生成相关问题",并且在示例中是在回答后添加"§§相关问题§§"部分。 在我的回答中,需要自然地添加相关内容。 </think> ### 回答:使用module_platform_driver的驱动是否可以不需要显式使用module_init 在Linux内核驱动开发中,`module_platform_driver`是一个宏,用于简化Platform驱动的注册过程。根据您提供的引用内容,我将逐步解释这个问题。 #### 1. **module_platform_driver的工作原理** - `module_platform_driver`宏是Linux内核提供的一个便利工具,用于自动注册和注销Platform驱动。它封装了驱动加载初始化(`module_init`)和卸载退出(`module_exit`)的逻辑。 - 从宏的定义(参考引用[2])可以看出: ```c #define module_platform_driver(__platform_driver) \ module_driver(__platform_driver, platform_driver_register, \ platform_driver_unregister) ``` 其中,`module_driver`宏会进一步展开: ```c #define module_driver(__driver, __register, __unregister, ...) \ static int __init __driver##_init(void) \ { \ return __register(&(__driver) , ##__VA_ARGS__); \ } \ module_init(__driver##_init); \ static void __exit __driver##_exit(void) \ { \ __unregister(&(__driver) , ##__VA_ARGS__); \ } \ module_exit(__driver##_exit); ``` 这表明,当您使用`module_platform_driver(my_driver)`时,宏会自动生成一个`__init`函数和一个`__exit`函数,并调用`module_init`和`module_exit`来注册这些函数。最终,它会调用`platform_driver_register`注册驱动[^2][^4]。 #### 2. **是否需要显式使用module_init** - **不需要**。因为`module_platform_driver`宏已经内部处理了`module_init`和`module_exit`的调用。它自动定义了模块的初始化函数和退出函数,并注册到内核中。 - 相比之下,如果不使用这个宏(如引用[3]中的方法一),您需要手动写驱动入口函数,并在其中调用`platform_driver_register`,然后用`module_init`注册该函数: ```c static int __init xxx_init(void) { return platform_driver_register(&xxx_driver); } module_init(xxx_init); ``` 但使用`module_platform_driver`后,这些步骤都被宏内部实现,因此您**不需要额外写`module_init`**[^1][^3][^4]。 - 引用[4]明确指出:`module_platform_driver`宏自动注册和注销驱动,避免了手动调用`platform_driver_register`和`platform_driver_unregister`的需要[^4]。 #### 3. **为什么可以省略module_init** - Linux内核的模块加载机制依赖于`module_init`来定义驱动的初始化入口点。`module_platform_driver`通过在宏展开时自动调用`module_init`,将生成的初始化函数(如`my_driver_init`)注册到内核。这意味着宏本身已经完成了`module_init`的工作。 - 这简化了代码:开发者只需写`module_platform_driver(my_driver)`一行,就能替代整个手动初始化过程。这减少了错误,并提高了代码可维护性[^2][^4]。 #### 4. **实际应用示例** - 如果您有一个Platform驱动`my_driver`,可以这样写: ```c static struct platform_driver my_driver = { .driver = { .name = "example", }, .probe = my_probe, .remove = my_remove, }; module_platform_driver(my_driver); ``` 这等价于手动写法: ```c 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`相关代码[^1][^3]。 #### 总结 - **是的,使用`module_platform_driver`的驱动不需要显式使用`module_init`**。因为该宏内部已经封装了`module_init`的调用,会自动生成并注册驱动的初始化函数。这减少了代码冗余,是Linux内核中推荐的做法,尤其适用于Platform驱动模型[^1][^2][^3][^4]。 如果您在开发中遇到具体问题(如宏展开错误或兼容性问题),建议检查内核版本和文档,因为宏的具体实现可能因内核版本而异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值