当前位置: 首页 > news >正文

【Linux内核模块】模块声明与描述

如果你见过内核模块的代码,肯定注意过那些以MODULE_开头的宏定义 ——MODULE_LICENSE("GPL")、MODULE_AUTHOR("Your Name")…… 这些看似不起眼的声明,其实是模块的身份证,记录着模块的关键信息。它们不仅能让内核识别模块的身份,还影响模块的功能权限。

目录

一、为什么需要模块声明?

1.1 内核需要认识模块​

1.2 声明不是可有可无的装饰​

1.3 模块声明的三大作用

二、核心声明:模块的法律文件

2.1 MODULE_LICENSE:许可证声明(必须有!)​

2.2 MODULE_AUTHOR:开发者信息(推荐)​

2.3 MODULE_DESCRIPTION:功能描述(推荐)​

2.4 MODULE_VERSION:版本信息(推荐)​

2.5 MODULE_ALIAS:别名(可选)​

三、扩展声明:让模块信息更完整​

3.1 MODULE_DEVICE_TABLE:设备匹配表(驱动模块必备)​

3.2 MODULE_SUPPORTED_DEVICE:支持的设备类型(可选)​

3.3 MODULE_INFO:自定义信息(灵活扩展)​

四、如何查看模块声明信息?—— modinfo命令实战​

4.1 基本用法​

4.2 输出结果解析

4.3 实用场景​

五、声明的潜规则:内核如何处理这些信息?​

5.1 许可证验证流程​

5.2 声明的存储位置​

5.3 声明与模块依赖​

六、实战示例:带完整声明的模块代码​

七、常见错误与避坑指南​

八、声明的最佳实践:写出规范的模块信息​


一、为什么需要模块声明?

1.1 内核需要认识模块​

想象你去参加一个技术会议,门口的签到表会记录你的姓名、公司、职位 —— 这些信息帮助主办方识别你的身份。内核模块加载时也一样,内核需要知道:​

  • 这个模块遵循什么许可证(是否允许使用内核的 GPL 符号)​
  • 谁开发的(出问题时找谁)​
  • 模块的功能是什么(是否与其他模块冲突)​

这些信息都通过模块声明来传递,缺少关键声明的模块,内核会拒绝接待(限制功能或直接加载失败)。​

1.2 声明不是可有可无的装饰​

新手常犯的错误是觉得这些声明无关紧要,甚至省略MODULE_LICENSE。但实际上:​

  • 没声明许可证的模块会被内核标记为污染内核(tainted kernel)​
  • 某些内核功能(如EXPORT_SYMBOL_GPL导出的符号)会对未声明 GPL 的模块关闭​
  • 缺少描述信息的模块,管理员用modinfo查看时会一脸茫然​

1.3 模块声明的三大作用

二、核心声明:模块的法律文件

2.1 MODULE_LICENSE:许可证声明(必须有!)​

这是唯一必须的声明,它的作用就像软件的版权协议,决定了模块能使用内核的哪些功能。​

常见许可证类型:​

  • MODULE_LICENSE("GPL"):最常用,遵循 GPL 协议,可使用内核中所有符号(包括EXPORT_SYMBOL_GPL导出的)。​
  • MODULE_LICENSE("GPL v2"):明确指定 GPL 版本 2,兼容性更好。​
  • MODULE_LICENSE("Dual BSD/GPL"):双许可证,既可以按 BSD 也可以按 GPL 协议使用。​
  • MODULE_LICENSE("Proprietary"):专有许可证(闭源),会被内核标记为污染,且无法使用 GPL-only 符号。​

不声明许可证的后果:​

加载模块时内核会报错:

module: module license 'unspecified' taints kernel.

更严重的是,模块将无法调用EXPORT_SYMBOL_GPL导出的函数(如大部分内核核心功能),等于被降级使用。​

2.2 MODULE_AUTHOR:开发者信息(推荐)​

格式:MODULE_AUTHOR("姓名或邮箱")​

作用:记录模块开发者,方便问题追溯。比如: 

MODULE_AUTHOR("Zhang San <zhangsan@example.com>");

当模块出现 bug 时,内核日志会包含开发者信息,方便联系维护者。​

2.3 MODULE_DESCRIPTION:功能描述(推荐)​

格式:MODULE_DESCRIPTION("模块功能说明")​

作用:用一句话描述模块的用途,比如: 

MODULE_DESCRIPTION("USB to serial converter driver");

管理员用modinfo查看时,能快速知道这个模块是干什么的,避免误删关键模块。​

2.4 MODULE_VERSION:版本信息(推荐)​

格式:MODULE_VERSION("x.y.z")​

作用:标记模块版本,方便管理升级。比如: 

MODULE_VERSION("1.2.3");

当系统中存在多个版本的模块时,这个声明能帮你区分它们。​

2.5 MODULE_ALIAS:别名(可选)​

格式:MODULE_ALIAS("别名")​

作用:给模块起个别名,方便modprobe自动加载。比如: 

MODULE_ALIAS("usb:v1234p5678d*dc*dsc*dp*ic*isc*ip*in*");

这个别名表示该模块支持 USB 厂商 ID 为 1234、产品 ID 为 5678 的设备,当系统插入该设备时,modprobe会自动加载此模块。​

三、扩展声明:让模块信息更完整​

除了核心声明,还有一些扩展声明能让模块信息更丰富,管理更方便。​

3.1 MODULE_DEVICE_TABLE:设备匹配表(驱动模块必备)​

用于驱动模块声明支持的设备,格式因总线类型而异。以 USB 驱动为例: 

static const struct usb_device_id my_usb_id_table[] = {{ USB_DEVICE(0x1234, 0x5678) },  // 支持的设备ID{ }  // 结束标记
};
MODULE_DEVICE_TABLE(usb, my_usb_id_table);

这个声明会让内核知道模块支持哪些设备,当对应的设备插入时,自动加载模块。​

3.2 MODULE_SUPPORTED_DEVICE:支持的设备类型(可选)​

格式:MODULE_SUPPORTED_DEVICE("设备类型描述")​

作用:简要说明模块支持的设备,比如: 

MODULE_SUPPORTED_DEVICE("USB to RS232 converters");

主要用于文档说明,不影响实际功能。​

3.3 MODULE_INFO:自定义信息(灵活扩展)​

格式:MODULE_INFO(键, "值")​

作用:添加自定义元信息,比如: 

MODULE_INFO(version, "1.2.3");
MODULE_INFO(release_date, "2024-05-20");

这些信息会被modinfo识别并显示,适合添加版本日期、测试状态等自定义内容。​

四、如何查看模块声明信息?—— modinfo命令实战​

模块声明的所有信息,都可以用modinfo命令查看,这是管理员管理模块的常用工具。​

4.1 基本用法​

编译模块后,执行: 

modinfo 模块名.ko

比如查看一个名为usb_serial.ko的模块:

modinfo usb_serial.ko

4.2 输出结果解析

filename:       /path/to/usb_serial.ko
license:        GPL v2
author:         Zhang San <zhangsan@example.com>
description:    USB to serial converter driver
version:        1.2.3
alias:          usb:v1234p5678d*dc*dsc*dp*ic*isc*ip*in*
supported:      external
depends:        
intree:         N
vermagic:       5.4.0-100-generic SMP mod_unload 

每一行对应一个模块声明:​

  • license → MODULE_LICENSE​
  • author → MODULE_AUTHOR​
  • description → MODULE_DESCRIPTION​
  • version → MODULE_VERSION​
  • alias → MODULE_ALIAS​

4.3 实用场景​

  • 检查模块是否声明了正确的许可证(避免权限问题)​
  • 确认模块版本(排查版本不兼容问题)​
  • 查看模块支持的设备(判断是否匹配当前硬件)​
  • 查找模块作者(报告 bug 或寻求帮助)​

五、声明的潜规则:内核如何处理这些信息?​

5.1 许可证验证流程​

当模块加载时,内核会:​

  1. 检查MODULE_LICENSE是否存在​
  2. 如果是 GPL 兼容许可证,标记模块为合规
  3. 如果是专有许可证或未声明,标记为污染内核(taint flag)​
  4. 根据合规性决定是否允许访问 GPL-only 符号​

这个过程确保内核的 GPL 许可证条款不被违反,也是为什么闭源驱动常被内核社区抵制的原因。​

5.2 声明的存储位置​

模块声明的信息被编译到.modinfo段(模块的一个特殊数据段)中,不影响模块的代码执行,只用于信息展示和内核验证。​

可以用objdump命令查看这个段的内容:

objdump -s -j .modinfo 模块名.ko

会看到类似这样的输出(二进制数据,包含所有声明信息)。​

5.3 声明与模块依赖​

modinfo显示的depends字段(依赖模块),虽然不是通过MODULE_宏声明的,但内核会根据模块引用的符号自动生成这个列表。比如模块 A 使用了模块 B 导出的符号,depends就会显示模块 B 的名称。​

六、实战示例:带完整声明的模块代码​

下面通过一个完整的模块示例,展示如何正确使用各种声明:​

1. 模块代码(serial_driver.c) 

#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>// 驱动支持的USB设备ID表
static const struct usb_device_id serial_id_table[] = {{ USB_DEVICE(0x0403, 0x6001) },  // FTDI设备ID{ USB_DEVICE(0x1a86, 0x7523) },  // 国产CH340设备ID{ }  // 结束标记
};
MODULE_DEVICE_TABLE(usb, serial_id_table);// 模块初始化函数
static int __init serial_init(void) {printk(KERN_INFO "Serial driver loaded\n");return 0;
}// 模块退出函数
static void __exit serial_exit(void) {printk(KERN_INFO "Serial driver unloaded\n");
}// 入口/出口声明
module_init(serial_init);
module_exit(serial_exit);// 核心声明
MODULE_LICENSE("GPL v2");  // 必须的许可证声明
MODULE_AUTHOR("Li Si <lisi@example.com>");  // 开发者信息
MODULE_DESCRIPTION("USB to TTL serial converter driver");  // 功能描述
MODULE_VERSION("2.1.0");  // 版本号// 扩展声明
MODULE_ALIAS("usb:v0403p6001d*dc*dsc*dp*ic*isc*ip*in*");
MODULE_ALIAS("usb:v1a86p7523d*dc*dsc*dp*ic*isc*ip*in*");
MODULE_INFO(release_date, "2025-07-01");  // 自定义发布日期
MODULE_SUPPORTED_DEVICE("FTDI/CH340 USB to Serial adapters");

2. 编译 Makefile 

obj-m += serial_driver.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)default:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(PWD) clean

3. 编译并查看声明  

# 编译模块
make# 查看声明信息
modinfo serial_driver.ko

输出结果会包含所有声明的信息,比如:

license:        GPL v2
author:         Li Si <lisi@example.com>
description:    USB to TTL serial converter driver
version:        2.1.0
alias:          usb:v0403p6001d*dc*dsc*dp*ic*isc*ip*in*
alias:          usb:v1a86p7523d*dc*dsc*dp*ic*isc*ip*in*
supported:      FTDI/CH340 USB to Serial adapters
release_date:   2024-06-01

七、常见错误与避坑指南​

1. 忘记声明MODULE_LICENSE​

症状:加载模块时内核警告「tainted kernel」,且无法使用某些功能。​

解决:立即添加MODULE_LICENSE("GPL")(或其他兼容许可证),重新编译模块。​

2. 许可证与符号不兼容​

症状:模块使用了EXPORT_SYMBOL_GPL导出的符号,但声明了非 GPL 许可证,加载失败。​

解决:将许可证改为 GPL 兼容类型(如GPL、LGPL)。​

3. 声明信息错误或过时​

症状:modinfo显示的设备 ID 与实际支持的设备不符,导致设备无法识别。​

解决:更新MODULE_DEVICE_TABLE和MODULE_ALIAS,确保与硬件 ID 匹配。​

4. 过度声明​

症状:添加了大量无关的MODULE_INFO,导致modinfo输出混乱。​

解决:只保留必要的声明,自定义信息按需添加(如版本、发布日期)。​

八、声明的最佳实践:写出规范的模块信息​

1. 必选声明清单​

  • ✅ MODULE_LICENSE:至少声明一个许可证(推荐 GPL v2)​
  • ✅ MODULE_DESCRIPTION:一句话说清模块功能​
  • ✅ 驱动模块必须加MODULE_DEVICE_TABLE:关联支持的硬件 ID​

2. 推荐声明清单​

  • MODULE_AUTHOR:方便他人联系开发者​
  • MODULE_VERSION:便于版本管理​
  • MODULE_ALIAS:支持自动加载(尤其是驱动模块)​

3. 风格建议​

  • 许可证声明放在所有声明的最前面(醒目)​
  • 描述信息简洁明了(不超过 50 个字符)​
  • 作者信息包含邮箱(如"Name <email>")​
  • 版本号遵循语义化版本(主版本。次版本。修订号)​

模块声明看似简单,实则是模块开发的基础礼仪—— 清晰的声明能让内核正确识别模块,让管理员轻松管理模块,让其他开发者快速理解模块。​

http://www.lryc.cn/news/595881.html

相关文章:

  • 【RK3576】【Android14】MIC开发调试
  • 杭州网站建设选哪家?派迪科技项目实力展示
  • Python 正则表达式在数据分析中的应用:实战指南
  • OpenCV基本的图像处理
  • AI助力临床医学科研创新与效率双提升丨临床医学日常工作、论文高效撰写与项目申报、数据分析与可视化、机器学习建模等
  • 深入解析 Pandas:Python 数据分析的强大工具
  • AWE2026启动:加码AI科技,双展区联动开启产业新格局
  • 小玩 Lifecycle
  • ESP32-Cam三脚架机器人:DIY你的智能移动监控平台
  • 单一职责原则(SRP):构建高质量软件的基石
  • 【接口自动化】掌握接口自动化:核心概念讲解(理论知识)
  • Java 大视界 -- Java 大数据在智能医疗医疗设备维护与管理中的应用(358)
  • 阁楼式货架:垂直空间革命下的仓储效率升级方案
  • 在线教育培训课程视频如何防下载、防盗录?
  • 企业级IIS配置手册:安全加固/负载均衡/性能优化最佳实践
  • 为什么使用扩展坞会降低显示器的最大分辨率和刷新率
  • Cloud 与 VPS 的区别:如何选择最适合你的服务器解决方案?
  • vmware vsphere esxi6.5 使用工具导出镜像
  • SecretFlow (3) --- 添加合作方并创建项目
  • python小工具:测内网服务器网速和延迟
  • IPv4枯竭时代:从NAT技术到IPv6的演进之路
  • 本地代理和服务器代理区别
  • 目标检测系列(六)labelstudio实现自动化标注
  • JVM:工具
  • C++ 中重载函数右值引用和左值引用匹配的优先级
  • IP43半加固笔记本L156H
  • YOLO12论文阅读:Attention-Centric Real-Time Object Detectors
  • Linux操作系统从入门到实战(十二)Linux操作系统第一个程序(进度条)
  • iOS组件化详解
  • 深入浅出控制反转与依赖注入:从理论到实践