news 2026/9/3 8:11:57

Linux内核中模块定义宏机制解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux内核中模块定义宏机制解析

在编写 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_initmodule_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_registerplatform_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 阶段还需要做额外初始化工作,使用模块宏的话,它的底层只能调用对应的registerunregister函数无法做其他操作
  • 对初始化/退出顺序有精细控制需求

在这些场景下,手写module_init/module_exit反而更清晰。所以需要具体情况具体分析

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 4:07:16

期货市流动性危机

期货市场杠杆踩踏与流动性危机 CME、上期所连续上调保证金,高杠杆多头集中爆仓,触发程序化链式止损,形成 “下跌→强平→再下跌” 的死亡螺旋,半小时内抛单规模超 380 亿美元,部分时段出现流动性枯竭、报价断层。 交割…

作者头像 李华
网站建设 2026/9/3 5:48:44

对贵金属市场的

对贵金属市场的直接冲击 价格联动与避险逻辑重构 白银同步拖累黄金、铂金、钯金等贵金属,黄金单日最大跌幅超 3%,贵金属整体波动率飙升至近 40 年高位,打破 “黄金强避险、白银高弹性” 的传统联动节奏。 白银兼具金融与工业属性,…

作者头像 李华
网站建设 2026/9/2 4:01:17

C#: 告别繁琐!轻松移除Word文档中的文本与图片水印

在日常工作和学习中,我们经常会遇到带有水印的Word文档。无论是“草稿”、“绝密”等文本水印,还是公司Logo、个人签名等图片水印,它们有时会影响文档的阅读体验,甚至在打印时造成不便。手动逐一删除这些水印不仅效率低下&#xf…

作者头像 李华
网站建设 2026/9/2 21:37:26

写论文软件哪个好?实测3类工具后,虎贲等考AI凭全流程赋能封神!

毕业季选写论文软件,就像“拆盲盒”:有的只懂降重却不会搭框架,有的能生成文字却缺乏真实数据,有的操作复杂堪比学编程……到底写论文软件哪个好?经过对“单一功能工具、通用AI模型、专业学术平台”3类主流工具的深度实…

作者头像 李华
网站建设 2026/9/3 4:10:07

springboot 快速体验

场景&#xff1a;浏览器发送**/hello**请求&#xff0c;返回"Hello,Spring Boot 3!"开发步骤 创建Maven工程添加依赖(springboot父工程依赖 , web启动器依赖)编写启动引导类(springboot项目运行的入口)编写处理器Controller启动项目 项目目录父工程 pom <?xml ve…

作者头像 李华