Linux内核中模块定义宏机制解析
在编写 Linux 设备驱动时,尤其是 platform、I2C、SPI 等总线驱动,我们经常会看到类似下面的写法:
module_platform_driver(my_driver);
这类宏看起来很“魔法”,但实际上它们只是 Linux 内核为了减少样板代码而提供的一种 driver helper macro,本文主要讲解这类宏的用法与机制
一、传统模块初始化方式
这里以platform驱动为例,传统的驱动写法通常是这样的:
static struct platform_driver my_platform_driver = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "my_driver",
},
};
static int __init my_init(void)
{
return platform_driver_register(&my_platform_driver);
}
static void __exit my_exit(void)
{
platform_driver_unregister(&my_platform_driver);
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
这是一个标准的驱动模板,有驱动的入口init函数与出口exit函数,并通过module_init和module_exit接口函数进行注册,这种写法的样板代码高度重复,几乎每一个platform驱动都是一模一样的,内核中存在大量这种固定模式的代码,非常适合使用宏来简化
二、模块定义宏的引入
为了解决上述问题,Linux 内核引入了一组 module driver helper macro,用于简化驱动的注册与注销过程。
以platform驱动为例,内核提供了
module_platform_driver(...)
虽然接口看着像是函数,但是他是由宏来实现的,使用该宏之后,上面的代码就可以简化为:
static struct platform_driver my_platform_driver = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "my_driver",
},
};
module_platform_driver(my_platform_driver);
MODULE_LICENSE("GPL");
可以看见,使用该宏之后,就不需要再手写__init和__exit注册与注销接口函数了,也不需要再显式调用platform_driver_register和platform_driver_unregister接口函数了,与传统写法完全相同
当然这种写法不仅仅只有platform驱动有,内核为不同的总线都提供了对应的宏定义
module_i2c_driver(my_i2c_driver);
module_spi_driver(my_spi_driver);
module_usb_driver(my_usb_driver);
module_pci_driver(my_pci_driver);
.......
他们遵循完全相同的设计思想:一个模块,只注册一个驱动,用一行宏搞定
三、本质解析
这里还是以platform驱动为例,module_platform_driver 是一个宏封装。我们可以打开内核源码kernel/include/linux/platform_device.h找到对应的宏,如果为其他总线驱动,需要到对应的头文件中查找,如下所示
/* module_platform_driver() - Helper macro for drivers that don't do
* anything special in module init/exit. This eliminates a lot of
* boilerplate. Each module may only use this macro once, and
* calling it replaces module_init() and module_exit()
*/
#define module_platform_driver(__platform_driver) \
module_driver(__platform_driver, platform_driver_register, \
platform_driver_unregister)
可以看见module_platform_driver宏中又使用了module_driver这个宏定义,这个宏定在kernel/include/linux/device/driver.h头文件中,定义代码如下
/**
* module_driver() - Helper macro for drivers that don't do anything
* special in module init/exit. This eliminates a lot of boilerplate.
* Each module may only use this macro once, and calling it replaces
* module_init() and module_exit().
*
* @__driver: driver name
* @__register: register function for this driver type
* @__unregister: unregister function for this driver type
* @...: Additional arguments to be passed to __register and __unregister.
*
* Use this macro to construct bus specific macros for registering
* drivers, and do not use it on its own.
*/
#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_driver宏中就可以看见对应的驱动注册与注销的模板了,这里主要就是通过宏拼接以及可变参数宏来实现,读者可以自行宏展开进行分析,所有的模块宏platform驱动、pci驱动、usb驱动等等,底层都是调用了module_driver进行宏替换与拼接组成最后的模块注册模板,这里就不再赘述了
四、使用场景
在主线内核中,这种写法已经成为事实标准,原因主要有:
- *减少样板代码
- 统一驱动风格
- 降低出错概率
- 代码审查更友好
对于维护者来说,一眼看到module_platform_driver(xxx_driver); 就能立刻知道这是一个“标准的 platform 模块驱动。
虽然 helper macro 很方便,但并非所有场景都适合。不建议使用的情况包括:
- 一个模块中注册 多个 driver
- 模块 init 阶段还需要做额外初始化工作,使用模块宏的话,它的底层只能调用对应的
register和unregister函数无法做其他操作 - 对初始化/退出顺序有精细控制需求
在这些场景下,手写 module_init / module_exit 反而更清晰。所以需要具体情况具体分析

浙公网安备 33010602011771号