Linux 内核模块是如何加载与卸载的

9次阅读

应优先使用modprobe而非insmod,因其能自动处理依赖、搜索路径、别名和配置;卸载时modprobe -r比rmmod更安全,可递归卸载依赖模块;验证需结合dmesg日志而非仅lsmod。

Linux 内核模块是如何加载与卸载的

加载模块:用 insmod 还是 modprobe?关键看依赖

直接加载一个孤立的 .ko 文件,insmod 能行;但绝大多数真实场景(比如网卡驱动usb 子系统)都存在依赖链,这时必须用 modprobe。它会自动读取 /lib/modules/$(uname -r)/modules.dep,递归加载所有前置模块——比如加载 uas(USB Attached SCSI)前,modprobe 会先确保 usbcorescsi_mod 已就位,而 insmod uas.ko 会直接报 Unknown symbol in module

  • insmod 只认绝对路径,不查模块搜索路径,也不能处理别名(alias)或参数配置
  • modprobe/lib/modules/$(uname -r)/kernel/ 开始找,支持 /etc/modprobe.d/*.conf 中定义的 aliasoptionsinstall 指令
  • 加载带参数的模块:sudo modprobe igb InterruptThrottleRate=3000sudo insmod /lib/modules/.../igb.ko InterruptThrottleRate=3000 更可靠,因为前者能触发配置文件中预设的校验逻辑

卸载模块:为什么 rmmod 常失败,而 modprobe -r 更安全

rmmod 是“硬拔”——它只检查模块自身的引用计数(used by 列),一旦发现非零(比如有进程正打开该模块创建的设备节点、或另一个模块在 module_depends 中声明了它),就拒绝卸载,报错 Error: Module xxx is in use。而 modprobe -r 会反向遍历整个依赖图,先卸载所有依赖当前模块的“子模块”,再清理目标模块,避免卡死。

  • 强制卸载(rmmod -f)极危险:若模块正在处理中断或持有自旋锁,可能引发 Kernel panic,尤其在生产环境应绝对避免
  • 卸载前务必确认无活跃使用:lsmod | grep ^xxxused by 是否为 0;lsof /dev/xxxfind /sys -name "*xxx*" 2>/dev/NULL 可辅助排查隐式占用
  • 某些模块(如 nvidia)被 X Server 或容器运行时锁定,需先停服务再卸载

验证与调试:光看 lsmod 不够,得盯住内核日志

lsmod 只告诉你模块“在列表里”,不代表初始化成功;真正可靠的判断依据是 dmesg 输出。模块的 init 函数里调用的 printk() 会出现在日志末尾,而错误(如内存分配失败、硬件 probe 失败)也会在这里暴露。

  • 加载后立即执行:dmesg | tail -20,查找 my_module:error/fail 关键字
  • 卸载后同样查 dmesg,确认 exit 函数是否执行完毕,有没有资源泄漏警告(如 “device not unregistered”)
  • modinfo my_module.ko 必须在加载前运行,它能提前暴露兼容性问题:比如 vermagic: 字段与当前 uname -r 不符,modprobe 会静默跳过,但 insmod 会报 Invalid module format

自动加载机制:硬件插入时谁在背后调用 modprobe

不是内核直接执行命令,而是通过 udev 事件驱动。当 USB 设备接入,内核生成 add@/devices/... 事件,udev 根据 /lib/udev/rules.d/80-drivers.rules 匹配 DRIVER=="?*",最终执行 modprobe $env{DRIVER}。这意味着:手动 modprobe 成功,不代表热插拔一定生效——规则文件缺失、udev 服务未运行、或模块未列入 modules.builtin 都会导致静默失败。

  • 测试热插拔行为,不要只插拔硬件,要同时监控:udevadm monitor --subsystem-match=pci --Propertydmesg -w
  • 若需开机自动加载,在 /etc/modules 中追加模块名(一行一个),比写 modprobe/etc/rc.local 更规范,且支持 systemdinitrd 阶段
  • 禁用某个模块自动加载(如屏蔽 nouveau):在 /etc/modprobe.d/blacklist.conf 中写 blacklist nouveau,再运行 sudo depmod -a

模块加载卸载看着只是几条命令,实际是内核符号解析、内存映射、依赖拓扑维护、用户空间协同的一整套机制。最容易被忽略的是:模块的“加载成功”和“功能可用”之间,隔着硬件 probe、设备注册、用户态服务配合三道关卡——别只盯着 lsmod 的输出。

text=ZqhQzanResources