暗色模式

Linux 内核模块管理:从 lsmod 到 modprobe 驱动加载与开机自动加载

技术教程
2026-09-01
4
0
本文要点
  • 内核模块是可按需加载/卸载的内核代码,驱动、文件系统、网络协议都以模块形式存在,lsmod 一行看清当前加载了哪些模块及引用计数
  • modinfo <模块> 查看模块元数据:filenamedepends(依赖)、licensedescriptionmodprobe -n -v 先预演加载过程,改系统前先看脚本
  • modprobe 会自动解析依赖并加载(本文 bonding 自动带上 tls),rmmod 只卸载引用计数为 0 的模块,modprobe -r 连带释放不再被引用的依赖
  • 开机自动加载写入 /etc/modules-load.d/*.conf,每行一个模块名;换内核后跑 depmod 重建依赖树
  • 模块加载失败用 dmesg 看内核日志定位,常见原因是缺失依赖、参数不匹配或版本不兼容

什么是内核模块:驱动即插即用

Linux 内核并没有把全部功能都编进内核镜像,而是把驱动、文件系统、网络协议等编译成一个个内核模块(扩展名 .ko,Modern 发行版通常再压缩为 .ko.zst),放在 /lib/modules/$(uname -r)/ 目录下,系统启动后按需加载、不用时卸载。

模块带来的好处:内核镜像体积小、功能可组合;换网卡/换存储插上就能用;开发驱动不用反复重启内核。代价是引入了依赖关系与加载失败排查问题——这正是本文要讲清楚的。

查看当前内核对应模块库:

ls /lib/modules/$(uname -r)/

会看到 kernel/(模块本体按子系统归类)、modules.dep(依赖关系索引)、modules.alias(设备别名索引)等文件,这些都是 depmod 生成的元数据,modprobe 加载时靠它们解析依赖。

lsmod:查看已加载的模块

lsmod 列出当前已加载的所有模块,三列含义:

  • Module:模块名
  • Size:占用的内存大小(字节)
  • Used by:被多少个引用引用;列出的是引用方模块名
lsmod | head -14

lsmod 查看已加载模块

注意 Used by 这一列是判断能否卸载的关键:nfsd 847872 3 表示有 3 个引用;而 sunrpc 802816 20 后面挂着一长串 nfsd,nfsv4,auth_rpcgss,...,被 20 处引用——这种模块想卸载都卸不掉,因为它支撑着整个 NFS 栈。引用计数不为 0 时 rmmod 会直接报 Module is in use

modinfo:模块的身份证

想知道某个模块是什么、依赖谁,用 modinfo。以网卡链路聚合驱动 bonding 为例(未加载也能查,元数据在模块文件里):

modinfo bonding | head -16

modinfo 查看 bonding 模块元数据

字段里最有用的是这几个:

  • filename:模块文件的实际路径,/lib/modules/6.8.0-136-generic/kernel/drivers/net/bonding/bonding.ko.zst
  • description:一句话说明模块用途
  • license:许可证,非 GPL 的驱动加载时内核会打 tainted 标记
  • depends:依赖哪些模块——本例是 tls,加载 bonding 之前必须先有 tls
  • vermagic:模块编译所依赖的内核版本与选项,和当前内核不符会加载失败

depends 这一行直接预告了后面的加载行为:modprobe bonding 时,系统会先加载 tls

modprobe:智能加载,自动解决依赖

内核加载模块其实有两个工具:insmod 只能加载指定文件、不解析依赖;modprobe 会查 modules.dep 依赖树,自动先把依赖加载好。日常用 modprobe 就够了。

动手加载前先预演,看它到底会执行什么:

modprobe -n -v bonding; modprobe bonding; lsmod | grep -E "bonding|tls"

-n 是 dry-run(只预演不执行),-v 打印将要执行的命令。结果分三段看:

modprobe 预演与加载:自动先加载 tls

  • 前两行 insmod ... 是预演输出的计划:modprobe 判定 bonding 依赖 tls,于是先 insmod tls.ko.zst,再 insmod bonding.ko.zst,依赖顺序完全自动
  • 第三、四行是真正 modprobe bonding 之后 lsmod 的结果:bonding 和 tls 都已加载,且 tls 155648 1 bonding——tls 被 bonding 引用,引用计数为 1

-n 去掉再跑一遍就是正式加载。预演习惯在排查"这条命令会不会碰依赖、会不会改系统"时特别有用。

卸载与引用计数:rmmod 与 modprobe -r

卸载用 rmmod <模块>,但它有个硬性前提:模块引用计数必须为 0(没有进程打开它的设备、没有其他模块依赖它),否则报 Module is in use。上面加载的 bonding 此刻没有被引用(bonding 253952 0),可以直接卸:

rmmod bonding
lsmod | grep -E "bonding|tls"

rmmod bonding 只卸载 bonding 本身;它依赖的 tls 此时引用计数已经归零,但 rmmod 不会自动动它。想要"卸载并连带清理不再被引用的依赖",用 modprobe -r

modprobe -r bonding
lsmod | grep -E "bonding|tls"

modprobe -r 会逆着依赖树卸载:先卸 bonding,再看 tls 引用计数是否归零、是否被其他模块使用,都不满足就一并卸掉——执行后 grep 什么都查不到,干净利落。

开机自动加载:/etc/modules-load.d/

驱动、内核功能如果希望开机就绪(比如自编译模块、要用的文件系统、ip_vs 之类),写在 /etc/modules-load.d/ 下的 .conf 文件里,每行一个模块名,systemd-modules-load 开机阶段会按文件名顺序读取加载:

# /etc/modules-load.d/bonding.conf
bonding

注意别放在 /etc/modules(那是 sysvinit 时代的旧写法),且文件不要写成模块文件路径,只写模块名。确认写法:

cat /etc/modules-load.d/bonding.conf

如果换了内核(如 apt upgrade 升级到新 kernel),模块库目录会多一套,记得跑 depmod -a 重建 modules.dep,否则新模块可能解析不到依赖。

加载失败怎么办:dmesg 定位

modprobe 失败时先看它的报错,再配合内核日志确认根因:

dmesg | tail -30

常见三类:

  • 模块不存在modprobe: FATAL: Module xxx not found——模块名拼错、或该模块在另一个内核版本目录下,检查 ls /lib/modules/$(uname -r)/
  • 依赖缺失:报找不到某个依赖,多半是 modules.dep 过期,跑 depmod -a 重建
  • 版本不兼容:加载时打印 disagrees about version of symbol 或 vermagic 不匹配——自己编译的模块和当前内核选项不一致,需要重新编译或换用发行版自带的模块

modprobe 是模块管理的唯一入口,配合 lsmod/modinfo/dmesg,绝大部分加载问题都能定位。

常用命令速查

命令作用
lsmod列出已加载模块及引用计数
modinfo <模块>查看模块元数据(路径、依赖、许可证等)
modprobe <模块>加载模块,自动解析并加载依赖
modprobe -r <模块>卸载模块,连带清理不再被引用的依赖
modprobe -n -v <模块>dry-run 预演加载,打印将执行的 insmod 命令
modprobe --show-depends <模块>只打印该模块的依赖清单
rmmod <模块>直接卸载(引用计数须为 0),不处理依赖
insmod <文件>加载指定模块文件,不解析依赖(极少用)
depmod -a重建模块依赖树 modules.dep
ls /lib/modules/$(uname -r)/查看当前内核的模块库与元数据文件

小结

内核模块管理就四板斧:lsmod 看现状、modinfo 看身世、modprobe 加载(自动解决依赖)、modprobe -r/rmmod 卸载(看引用计数)。动手改系统前养成 modprobe -n -v 预演的习惯,开机要加载的模块写进 /etc/modules-load.d/,加载失败用 dmesg 看根因——这套流程能覆盖日常 90% 的模块场景。本文的 bonding 例子还顺带说明了一个事实:现代发行版的内核模块是"按需组装"的,你的系统里加载了哪些模块、谁依赖谁,用 lsmodmodinfo 一目了然。

参考资料

发表评论

暂无评论,快来抢沙发吧!