4. 模块加载与卸载:insmod、rmmod、modprobe 命令详解与模块依赖管理
内核模块编译完成后(.ko 文件),需要动态加载到运行中的内核才能生效。Linux 提供了 insmod、rmmod、modprobe 三组核心命令,分别对应模块的加载、卸载与依赖解析。本章深入讲解每条命令的工作原理、使用场景以及模块依赖管理机制。
4.1 模块加载命令:insmod 与 modprobe
insmod 是最直接的模块加载命令,它通过 init_module() 系统调用将模块二进制内容插入内核空间。但 insmod 不处理依赖关系,如果模块引用了其他模块导出的符号,必须先手动加载依赖模块。
核心区别: insmod 加载指定路径的 .ko 文件,不解析依赖;modprobe 根据模块名查找并自动加载所有依赖模块。
4.1.1 insmod 命令用法
# 加载当前目录下的模块
sudo insmod ./hello.ko
# 加载带参数的模块
sudo insmod ./gpio_led.ko gpio_pin=17 blink_rate=500
# 查看加载是否成功(无输出表示成功)
lsmod | grep hello
| 参数/选项 | 说明 |
|---|---|
./module.ko |
模块文件路径(必须指定完整或相对路径) |
param=value |
向模块传递参数(需模块内用 module_param 声明) |
-f (force) |
强制加载,即使内核版本不匹配或模块签名验证失败 |
⚠ 生产环境禁止使用 -f 强制加载: 强制加载会绕过版本校验和签名验证,可能导致内核崩溃(kernel panic)或数据损坏。仅用于开发调试。
4.1.2 modprobe 命令与依赖解析
modprobe 是更智能的模块加载工具,它依赖 /lib/modules/$(uname -r)/ 目录下的模块树和 modules.dep 依赖文件。加载模块时自动递归解析所有依赖。
# 按模块名加载(自动查找 .ko 文件并处理依赖)
sudo modprobe usb_storage
# 查看 modprobe 实际加载了哪些模块
sudo modprobe -v usb_storage
# 显示模块依赖关系(不实际加载)
sudo modprobe --show-depends usb_storage
💡 最佳实践: 开发阶段使用
insmod 加载自己编译的模块;生产环境或加载系统自带模块时使用 modprobe。
4.2 模块卸载命令:rmmod 与 modprobe -r
卸载模块时,内核会检查模块的引用计数(refcnt)。只有当引用计数为 0 时,模块才能被安全卸载。rmmod 和 modprobe -r 都调用 delete_module() 系统调用,但行为略有不同。
4.2.1 rmmod 命令
# 卸载指定模块
sudo rmmod hello
# 强制卸载(即使引用计数不为 0)
sudo rmmod -f hello
# 卸载并打印详细信息
sudo rmmod -v hello
| 场景 | 命令 | 说明 |
|---|---|---|
| 正常卸载 | sudo rmmod <module> |
引用计数必须为 0 |
| 强制卸载 | sudo rmmod -f <module> |
危险操作,可能造成内核不稳定 |
| 卸载并清理依赖 | sudo modprobe -r <module> |
递归卸载不再被使用的依赖模块 |
4.2.2 卸载失败常见原因
- 模块正被使用: 其他模块或进程持有该模块的引用,
lsmod可查看引用计数。 - 模块是内置模块: 编译进内核(
y)的模块无法卸载。 - 模块有设备文件打开: 用户空间程序打开了模块注册的设备节点。
调试技巧: 使用
lsmod | grep <module> 查看模块当前引用计数;使用 cat /proc/modules 获取更详细的模块状态信息。
4.3 模块依赖管理
模块依赖关系由符号导出(EXPORT_SYMBOL)和符号引用决定。内核在模块加载时通过 符号表解析 建立依赖图。
4.3.1 依赖关系生成流程
- 编译阶段: 每个
.ko文件包含.modinfo段,记录依赖的其他模块名。 - 安装阶段:
depmod工具扫描/lib/modules/$(uname -r)/下所有模块,生成modules.dep文件。 - 加载阶段:
modprobe读取modules.dep,按拓扑顺序加载依赖模块。
# 手动更新模块依赖(安装新模块后必须执行)
sudo depmod -a
# 查看模块依赖关系
cat /lib/modules/$(uname -r)/modules.dep | grep usb_storage
4.3.2 依赖关系可视化
4.4 模块信息查看与调试
加载/卸载前后,使用以下命令验证模块状态:
| 命令 | 查看内容 | 示例输出 |
|---|---|---|
lsmod |
当前加载的所有模块、大小、引用计数、被谁使用 | hello 16384 0 - Live 0xffffffffc0000000 |
modinfo <module> |
模块作者、描述、许可证、依赖、参数、别名 | depends: i2c-core, gpiolib |
cat /proc/modules |
与 lsmod 相同,但格式更原始 | hello 16384 0 - Live 0xffffffffc0000000 |
dmesg | tail |
内核日志,查看模块加载/卸载时的 printk 输出 | [12345.678] Hello, world from kernel module! |
4.5 模块加载/卸载完整流程总结
4.6 实战案例:依赖模块的加载与卸载
假设有两个模块:gpio_core(提供 GPIO 操作函数)和 led_driver(依赖 gpio_core)。
# 1. 查看模块依赖关系
modinfo led_driver
# 输出: depends: gpio_core
# 2. 错误方式:直接加载 led_driver(insmod 会失败)
sudo insmod ./led_driver.ko
# 输出: insmod: ERROR: could not insert module led_driver.ko: Unknown symbol in module
# 3. 正确方式:先加载依赖模块
sudo insmod ./gpio_core.ko
sudo insmod ./led_driver.ko
# 4. 使用 modprobe 自动处理依赖
sudo modprobe led_driver # 自动加载 gpio_core
# 5. 卸载(必须先卸载 led_driver)
sudo rmmod led_driver
sudo rmmod gpio_core
# 6. 使用 modprobe -r 递归卸载
sudo modprobe -r led_driver # 同时卸载 gpio_core(如果不再被使用)
💡 开发建议: 在模块开发阶段,建议在
Makefile 中添加 depmod 自动更新依赖,然后使用 modprobe 加载测试,避免手动处理依赖顺序的麻烦。