1.内核模块
1. 内核模块概念
a.什么是内核模块
内核模块是操作系统中动态加载到内核的代码组件,用于扩展内核功能而无需重新编译或重启系统。
1. 定义与作用
- 动态扩展:内核模块允许在运行时添加或移除功能,如设备驱动、文件系统支持或网络协议。
- 无需重启:加载模块后立即生效,避免系统停机,提升灵活性。
2. 运行环境
- 内核空间:模块在内核态运行,直接访问硬件和核心资源,与用户程序隔离(用户态通过系统调用交互)。
- 高权限操作:可执行底层操作(如中断处理、内存管理),但错误可能导致系统崩溃。
3. 可加载模块
可加载模块(Loadable Kernel Module, LKM)是操作系统内核的一种动态扩展机制,允许在不重新编译或重启系统的前提下,将代码(如设备驱动、文件系统、网络协议等)按需加载到内核中运行。
4. 总结
内核模块是增强操作系统功能的动态组件,通过灵活加载机制支持硬件扩展和系统优化。
b. 宏&微&混合 内核
1. 宏内核
核心理念:
- 高度集成:所有核心功能(进程管理、内存管理、文件系统、设备驱动等)均运行在内核空间,共享同一地址空间,通过直接函数调用交互
- 高性能:服务调用无需跨进程通信(IPC),延迟极低(纳秒级),适合实时性要求高的场景
- 模块化有限:虽然支持动态加载内核模块(LKM),但整体代码紧密耦合,扩展性较差
典型应用:Linux、早期VxWorks
2. 微内核
核心理念:
- 最小化内核:仅保留核心功能(进程调度、内存管理、IPC),其他服务(文件系统、驱动等)作为用户态进程运行
- 消息传递机制:服务间通过IPC通信,隔离性强但可能引入性能开销
- 模块化设计:服务可独立加载/卸载,提升系统灵活性和安全性
典型应用:QNX、HarmonyOS
3. 混合内核
核心理念:
- 折中设计:结合宏内核的高效性与微内核的隔离性,部分核心服务运行于内核态,非关键服务移至用户态
- 性能与安全平衡:例如将文件系统、网络协议栈保留在内核空间,而驱动或部分服务以用户态进程运行
典型应用:Windows NT、macOS
2.内核模块构成
a. 内核模块构成
1)核心
a. 头文件
内核专用头文件主要位于kernel/include/linux/*
和CPU架构相关kernel/arch/$(ARCH)/include/*
linux/module.h
- 提供模块相关的API接口
linux/init.h
- 初始化,清理相关接口
b. 加载/卸载宏
宏所在头文件是linux/module.h
;
-
#define module_init(x) __initcall(x);
- 指定模块函数入口,主要完成模块初始化工作
- 模块入口函数:
int __init func_init(void);
- 功能:模块被加载动内核时,入口函数自动被内核执行,模块入口函数使用
__init
声明 - 函数参数:
void
- 返回值类型:
int
一般返回errno
,可根据perror()
进行解析 - 使用:
module_init(func_init)
- 功能:模块被加载动内核时,入口函数自动被内核执行,模块入口函数使用
-
#define module_exit(x) __exitcall(x);
- 指定模块卸载函数入口,主要完成模块资源释放和模块的卸载
- 模块退出函数:
void __exit func_exit(void);
- 功能:模块在退出时自动调用,退出函数使用
__exit
声明 - 函数参数:
void
- 返回值类型:
void
- 使用:
module_exit(func_exit)
- 功能:模块在退出时自动调用,退出函数使用
c. 许可声明
宏所在头文件是linux/module.h
;
#define MODULE_LICENSE(_license) MODULE_FILE MODULE_INFO(license, _license)
- 功能:标注模块许可类型
- 宏参数:开源许可协议类型的字符串
- 使用:
MODULE_LICENSE("GPL");
d. 最简内核结构
simplemod.c
#include <linux/module.h>
#include <linux/init.h>static int __init simplemod_init(void) {return 0;
}static void __exit simplemod_exit(void) {
}module_init(simplemod_init); // 模块初始化函数
module_exit(simplemod_exit); // 模块退出函数MODULE_LICENSE("GPL");
Makefile
ARCH=arm64 # 架构
CROSS_COMPILE=aarch64-none-linux-gnu- # 交叉编译工具
obj-m += simplemod.o # 模块
KDIR := /project/mycode/rockchip-sdk/kernel # 内核源码路径.PTHONY:all clean# 编译
all:$(MAKE) -C $(KDIR) M=$(CURDIR) modules
# 清除
clean:$(MAKE) -C $(KDIR) M=$(CURDIR) clean
2)模块描述信息
宏所在头文件是linux/module.h
;
描述信息会通过modinfo
命令显示,帮助用户了解模块的用途。
a. 模块作者
#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author)
- 功能:声明内核模块的作者
- 宏参数类型:字符串
- 使用:
MODULE_AUTHOR("ah@email");
b. 描述信息
#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description)
- 功能:提供模块的简要说明,方便其他开发者或用户理解模块的用途
- 宏参数类型:字符串
- 使用:
MODULE_DESCRIPTION("This is description");
c. 模块起别名
#define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias)
- 功能:为模块定义一个别名
- 宏参数类型:字符串
- 使用:
MODULE_ALIAS("zhf");
d. 模块依赖关系
#define MODULE_SOFTDEP(_softdep) MODULE_INFO(softdep, _softdep)
- 功能:告知模块依赖关系
- 宏参数类型:字符串
- 使用:
MODULE_SOFTDEP("pre: module-foo post: module-baz");
e. 模块版本号
#define MODULE_VERSION(_version) MODULE_INFO(version, _version)
- 功能:定义模块的版本
- 宏参数类型:字符串
- 使用:
MODULE_VERSION("1.0");
b. 模块相关操作
1)如何编译
a. 源码外-编译成模块
编译Linux内核模块的makefile
ARCH=arm64 # 架构
CROSS_COMPILE=aarch64-none-linux-gnu- # 交叉编译工具
obj-m += simplemod.o # 模块
KDIR := /project/mycode/rockchip-sdk/kernel # 内核源码路径.PTHONY:all clean# 编译
all:$(MAKE) -C $(KDIR) M=$(CURDIR) modules
# 清除
clean:$(MAKE) -C $(KDIR) M=$(CURDIR) clean
核心内容介绍(编译内核模块的最小make指令):
obj-<X>
:编译过程中,程序会被编译为xxx.o
文件,在根据<X>
可选参数将xxx.o
文件构建或链接成制定的文件。obj-
:指定某个文件或目录是构建过程中的一个中间目标<X>
:可取值y、m、nm
:栗子obj-m += xxx.o
将xxx.o
文件构建成模块xxx.ko
y
:栗子obj-y += xxx.o
,将xxx.o
编译进内核n
:栗子obj-n += xxx.o
- 另一种用法:
obj-<X> += dir/
接目录
$(MAKE) -C $(KDIR) M=$(CURPWD) modules
$(MAKE) -C $(KDIR) M=$(CURPWD) clean
MAKE
:是一个特殊的宏变量、代表make
本身;使用MAKE
变量有几个好处:a.可移植性;b.递归调用;c.选项传递-C $(KDIR)
:-C
是change director,如含义-C $(KDIR)
指向make跳转到KDIR
目录下,KDIR
是一个变量,记录路径,该路径一般写内核源码路径M=$(CURDIR)
:M
用于指定将编译的模块源码路径,CURDIR
是makefile中的一个宏变量,不赋值则只想当前目录的绝对路径modules
:编译成xxx.ko
模块clean
:清除由make生成的文件
b. 源码内-编译成模块
详细描述见Kconfig子系统
c. 编译进内核
详细描述见Kconfig子系统
2)操作指令
a. 加载:insmod
含义:加载模块
指令格式:insmod filename
b. 查看已加载模块:lsmod
含义:列出加载的内核模块
指令格式:lsmod
c. 查看模块信息:modinfo
含义:模块信息
指令格式:modinfo [options] filename [args]
参数 | 作用 | 示例 |
---|---|---|
无参数 | 显示模块的所有元信息(作者、描述、许可证、依赖等) | modinfo ext4 |
-a | 显示模块作者信息(等价于 -F author ) | modinfo -a my_module |
-d | 显示模块描述(等价于 -F description ) | modinfo -d vboxdrv |
-l | 显示模块许可证(等价于 -F license ) | modinfo -l nvidia |
-p | 显示模块支持的参数(等价于 -F parm ) | modinfo -p e1000 |
-n | 显示模块的文件路径 | modinfo -n ext4 |
-0 | 使用 \0 (空字符)分隔输出字段,便于脚本解析 | modinfo -0 -F version my_module |
-F <字段> | 指定要显示的字段(支持 author , description , license , vermagic 等) | modinfo -F version iwlwifi |
-k <内核版本> | 指定要查询的内核版本(需模块路径包含该版本) | modinfo -k 5.15.0-86-generic /path/to/module.ko |
--set-version <版本> | 强制指定内核版本(覆盖自动检测) | modinfo --set-version=5.15.0-86-generic my_module |
d. 卸载:rmmod
含义:卸载模块
指令格式:rmmod [options] modulename ...
参数 | 作用 | 示例 |
---|---|---|
无参数 | 直接卸载模块 | rmmod modulename |
-f | 强制卸载模块 | rmmod -f modulename |
-w | 等待模块使用计数归零后自动卸载 | rmmod -w modulename |
-a | 移除所有未使用的模块 | rmmod -a |
-v | 显示版本 | rmmod -v |