1.内核模块

发布于:2025-08-04 ⋅ 阅读:(12) ⋅ 点赞:(0)

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、n
      • m:栗子obj-m += xxx.oxxx.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
e. modprobe


网站公告

今日签到

点亮在社区的每一天
去签到