本文要点
- 内核模块是可按需加载/卸载的内核代码,驱动、文件系统、网络协议都以模块形式存在,
lsmod一行看清当前加载了哪些模块及引用计数 modinfo <模块>查看模块元数据:filename、depends(依赖)、license、description;modprobe -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
注意 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
字段里最有用的是这几个:
- 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 打印将要执行的命令。结果分三段看:

- 前两行
insmod ...是预演输出的计划:modprobe判定 bonding 依赖 tls,于是先 insmodtls.ko.zst,再 insmodbonding.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 例子还顺带说明了一个事实:现代发行版的内核模块是"按需组装"的,你的系统里加载了哪些模块、谁依赖谁,用 lsmod 和 modinfo 一目了然。
评论 (0)
暂无评论,快来抢沙发吧!