1-Linux驱动开发-内核模块介绍
内核概念
微内核(Micro Kernel)和 宏内核(Monolithic Kernel)

微内核架构中,内核只提供操作系统核心功能。微内核具有动态扩展性强的优点。代表Windows 操作系统。
宏内核架构是将上述包括微内核以及微内核之外的应用层 IPC、文件系统功能、设备驱动模块都编译成一个整体。想要修改、增加内核某个功能时(如增加设备驱动程序)都需要重新编译一遍内核。代表linux 操作系统。为了解决这一缺点,linux 中引入了内核模块这一机制。
| 对比项 | 内核 (Kernel) | 内核模块 (Kernel Module) |
|---|---|---|
| 本质 | 系统的核心和主体,管理所有硬件和软件资源。 | 内核的扩展插件,用于添加特定功能。 |
| 编译方式 | 被编译成一个单一、完整的映像文件(vmlinuz 或 zImage)。 | 被编译成独立的 .ko 文件 (Kernel Object)。 |
| 加载时机 | 系统启动时由 Bootloader 加载到内存中,并一直运行。 | 系统运行过程中按需动态加载(insmod 或 modprobe)。 |
| 依赖关系 | 独立存在,是所有进程和模块运行的基础。 | 不能独立运行,必须依赖一个已经运行的内核。 |
| 主要功能 | 提供最核心的服务,如:进程调度、内存管理、中断处理等。 | 提供非核心或特定的功能,最常见的就是设备驱动、文件系统支持等。 |
内核模块
为了解决 Linux 宏内核架构下,将所有驱动编入内核导致其过于“臃肿”的问题。内核模块允许动态地向内核添加功能。
好处:
- 提高灵活性: 可以在运行时按需加载或卸载功能。
- 方便开发: 驱动开发人员可以单独编译和测试模块,无需重新编译整个内核或重启系统。
- 节省空间: 模块文件(ELF 文件)无需存放在开发板上,可以通过 NFS 等方式远程加载。
内核模块LKM (Loadable Kernel Module) 是一种在内核运行时加载代码来实现特定功能的机制。模块被加载后,就成为内核的一部分,在内核空间运行,但它本身不被编译进内核映像中。
内核工作机制
内核模块,经过编译,最终形成.ko 为后缀的 ELF (Excutable And Linking Format) 文件。是一种普通的可重定位目标文件。这类文件包含了代码和数据,可以被用来链接成可执行文件或共享目标文件。
四个命令可以分为两大类:
内核模块的“运行时管理”(在系统上加载、卸载、查看):lsmod, insmod, modprobe
内核模块的“开发时分析”(在编译后查看 .ko 文件内部):readelf
===================================
运行时管理 (lsmod, insmod, modprobe):在 Linux 系统上实际操作内核模块时使用
1.lsmod (List Modules):列出(List)当前已经加载到内核中的所有模块。
原理:读取并格式化 /proc/modules 这个虚拟文件的内容。

Module:模块的名称。
Size:模块占用的内存大小(字节)。
Used by:0 表示该模块已加载,但当前没有程序在使用它。>0 表示它被内核或其他模块使用的次数(引用计数)。>Used by 列表显示了哪些其他模块依赖于它。
2.insmod (Insert Module):“强行”插入一个内核模块。
insmod [模块文件的完整路径]
sudo insmod /home/user/my_driver/hello_world.ko
它不会自动处理依赖关系。
如果 hello_world.ko 依赖另一个模块(比如 common_funcs.ko),而 common_funcs.ko 还没有被加载,insmod 命令将会失败。
必须手动先 insmod common_funcs.ko,然后再 insmod hello_world.ko。
主要用于开发和调试,当你正在测试一个不在标准模块路径下的、刚刚编译好的 .ko 文件时。
3.modprobe (Module Probe)
智能地”加载(和卸载)内核模块。
modprobe [模块名称] (注意:这里是模块名,不是文件名)
sudo modprobe usb_storage
sudo modprobe -r usb_storage
它会读取 /lib/modules/$(uname -r)/modules.dep 文件(一个模块依赖数据库),如果发现 usb_storage 依赖 uas,它会自动先加载 uas,然后再加载 usb_storage。
它会自动去标准的模块目录(/lib/modules/$(uname -r)/)下查找模块文件,你不需要提供完整路径。
===================================
开发时分析 (readelf)
内核模块(.ko 文件)是一种 ELF (Executable and Linkable Format) 文件。readelf 是一个用于显示任何 ELF 文件信息的标准工具。
readelf (Read ELF):主要用于开发和调试。
readelf -h hello_world.ko (Header): 显示ELF头信息。确认这个 .ko 文件是为哪个架构编译的。如果你在 x86 电脑上编译了 ARM 的驱动,想加载到 STM32MP1 上,这个信息就能帮你提前发现错误。
readelf -S hello_world.ko (Sections): 显示文件的所有“节”。.text: 存放编译后的机器代码。.data: 存放已初始化的全局变量。.bss: 存放未初始化的全局变量。.modinfo: 模块信息节。存放了模块的作者、描述、许可证(license=GPL)等。
readelf -s hello_world.ko (Symbols): 显示“符号表”。符号表就是这个文件中的函数和变量的“清单”。你可以看到你的模块导出(Export)了哪些函数(供其他模块使用,用 EXPORT_SYMBOL 标记)。可以看到你的模块导入(Import)了哪些内核函数(比如 printk, kmalloc 等)。如果这里显示某个符号是 UND (Undefined),就意味着它依赖一个内核中不存在的函数,加载时就会出错(“Unresolved symbol”)。
===================================
开发流程:
在 PC 上交叉编译驱动,生成 my_driver.ko。
用 readelf -h my_driver.ko 检查它是否是 ARM 架构。
用 readelf -s my_driver.ko 检查它是否有不支持的依赖。
把 my_driver.ko 传到开发板上。
在开发板上,用 sudo insmod my_driver.ko 来快速测试它。
测试通过,把它安装到 /lib/modules/... 目录,然后用 sudo modprobe my_driver 来加载它。
