news 2026/7/8 13:53:12

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/7/1 22:10:34

企业级NTP客户端配置指南:基于内部NTP服务器的实践

引言 在分布式系统中,时间同步是保障业务一致性的关键基础设施。当企业已部署内部NTP服务器时,客户端的精准配置和有效验证尤为重要。本文将系统介绍客户端配置NTP服务的完整流程,涵盖配置方法、验证手段及关键注意事项,帮助运维…

作者头像 李华
网站建设 2026/6/30 21:31:23

智能楼宇漏水防护!水箱水泵机安全监测方案来了

智能楼宇里的水箱与水泵机,是给排水系统的关键组件,它们的稳定运转直接关系到楼宇供水是否顺畅。可实际使用中,漏水问题难免发生,若没能及时察觉并处理,不仅会打乱正常供水节奏,还可能引发设备损毁、电路短…

作者头像 李华
网站建设 2026/7/7 17:29:00

Linux05:防火墙的端口协议设置

文章目录一、防火墙是否运行二、运行防火墙二、防火墙设置1.临时开放端口(重启失效)2.开放端口(永久有效)3.关闭端口三、重载防火墙配置四、查看防火墙状态一、防火墙是否运行 systemctl status firewalld二、运行防火墙 system…

作者头像 李华
网站建设 2026/7/6 20:24:08

Win11 磁盘空间不足的真正原因:不只是 C 盘满那么简单

目录 一、一个常见误区:磁盘满 ≠ 装的软件多 二、Win11 磁盘空间不足的 7 个真实原因(核心) 1.Windows 更新缓存:最大空间黑洞 为什么会这样? 2.系统临时文件:长期不清理会“慢性膨胀” 3.虚拟内存&…

作者头像 李华
网站建设 2026/7/1 9:36:28

这款 IM 即时通讯也太好用了,聊天天花板

谁还在只用普通社交软件聊天啊😭挖到一款无广告、强隐私、全端同步的 IM 即时通讯工具,日常沟通 / 团队唠嗑都适配,用了就回不去! ✅ 端到端加密,聊天内容只有收发双方可见,阅后即焚、消息无痕擦除&#x…

作者头像 李华