龙岗附近做网站公司,银行网站开发,宿迁房产,django网站开发视频在编写 Linux 设备驱动时#xff0c;尤其是 platform、I2C、SPI 等总线驱动#xff0c;我们经常会看到类似下面的写法#xff1a; /* by yours.tools - online tools website : yours.tools/zh/utf8.html */ module_platform_driver(my_driver);这类宏看起来很“魔法”…在编写 Linux 设备驱动时尤其是 platform、I2C、SPI 等总线驱动我们经常会看到类似下面的写法/* by yours.tools - online tools website : yours.tools/zh/utf8.html */ module_platform_driver(my_driver);这类宏看起来很“魔法”但实际上它们只是 Linux 内核为了减少样板代码而提供的一种driver helper macro本文主要讲解这类宏的用法与机制一、传统模块初始化方式这里以platform驱动为例传统的驱动写法通常是这样的/* by yours.tools - online tools website : yours.tools/zh/utf8.html */ 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 dont 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 dont 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反而更清晰。所以需要具体情况具体分析