Linux 内核本身只包含最核心的功能,声卡、网卡、文件系统驱动、防火墙模块、虚拟化模块这些,大多以内核模块(kernel module)的形式在需要时才加载。这个设计的目的是让内核保持精简,同时又能灵活扩展。但对运维来说,它带来了一些必须掌握的操作:怎么加载一个模块、怎么让它在开机时自动加载、怎么给模块传参数、怎么屏蔽不想要的模块,以及模块加载失败时怎么排查。
很多真实故障都和内核模块有关:网卡识别不到是驱动模块没加载,Docker 起不来是 overlay 或 br_netfilter 模块没开,iptables 规则不生效是 nf_conntrack 或相关模块缺失,GPU 不可用是 nvidia 模块没装好。这篇文章系统讲一遍内核模块的管理:lsmod、modinfo、modprobe、insmod/rmmod、depmod,开机自动加载和黑名单的配置位置,几个常见模块的作用,以及"模块被占用卸不掉"这类报错的处理。看完你应该能独立处理绝大多数和模块相关的问题。
内核模块是什么
内核模块是可以在运行时动态加载进内核、也可以动态卸载的代码片段。加载后,它就运行在内核态,和内核是同一权限级别。上面这些场景都靠它:
- 硬件驱动:网卡、RAID 卡、GPU、无线网卡;
- 文件系统:
xfs、ext4、btrfs、overlay; - 网络功能:
iptables的nf_conntrack、br_netfilter,bonding、vlan; - 杂项:
ip_vs(LVS 负载均衡)、nvidia(GPU 计算)、zram(内存压缩)。
用 lsmod 看当前加载了哪些模块:
lsmod
# 输出三列:模块名、占用内存大小、被引用次数 引用它的模块
Module Size Used by
xfs 1495040 3
nf_conntrack 172032 1 nf_nat
nvidia 55734272 42
Used by 那一列很关键:数字大于 0 表示有进程或其他模块正在使用它,这时不能卸载。
# 系统里可用的模块文件位置
ls /lib/modules/$(uname -r)/kernel/
# 常见子目录:drivers/ fs/ net/ kernel/
查看模块详情:modinfo
modinfo 显示一个模块的所有元数据:文件路径、版本、依赖、参数、作者、许可证:
modinfo nf_conntrack
modinfo bonding
输出里有几个字段值得关注:
filename: /lib/modules/5.14.0-362.el9.x86_64/kernel/net/netfilter/nf_conntrack.ko.xz
license: GPL
description: Netfilter connection tracking
depends: nf_defrag_ipv4
parm: tstamp:Enable connection tracking flow timestamping. (bool)
filename:模块文件的实际路径和格式(.ko、.ko.xz是压缩的)。depends:这个模块依赖哪些其他模块,modprobe会自动处理。parm:模块支持的参数,加载时可以通过它调整行为。
modinfo -p 模块名 只列参数,modinfo -F depends 模块名 只看依赖,写脚本时很方便。
加载与卸载
有两个层次的加载命令,区别很重要:
| 命令 | 是否处理依赖 | 是否查模块路径 | 适用场景 |
|---|---|---|---|
insmod |
否 | 否,须给全路径 | 指定某个 .ko 文件调试 |
modprobe |
是 | 是,自动在 /lib/modules 找 |
日常加载,推荐 |
rmmod |
否 | 否,只删不检查依赖 | 简单卸载 |
modprobe -r |
是 | 是 | 日常卸载,推荐 |
推荐始终用 modprobe 系列,它会自动解析依赖、从标准路径查找。insmod 需要给绝对路径且不处理依赖,只在调试自编译模块时用。
# 加载模块
sudo modprobe nf_conntrack
sudo modprobe -v nf_conntrack # -v 显示详细过程
# 加载模块并传参数(参数名=值,多个用空格分隔或多次传)
sudo modprobe bonding mode=802.3ad miimon=100
# 卸载模块
sudo modprobe -r nf_conntrack
sudo rmmod nf_conntrack # 等价,但不处理依赖
# 查看某模块的依赖关系
modprobe --show-depends bonding
# 检查模块是否存在(不加载)
modinfo -n bonding
depmod 负责生成模块依赖表(modules.dep),modprobe 依赖它来决定加载顺序和路径:
# 重新扫描并生成依赖表(安装了新模块后执行)
sudo depmod -a
depmod -n | head # -n 只打印不写入,便于预览
# 查看依赖表
cat /lib/modules/$(uname -r)/modules.dep | grep nf_conntrack
开机自动加载
临时 modprobe 的模块重启就没了。要在开机时自动加载,配置在 /etc/modules-load.d/ 目录下:
# 创建一个配置文件,每行一个模块名
sudo tee /etc/modules-load.d/my-modules.conf <<'EOF'
bonding
nf_conntrack
ip_vs
EOF
文件里 # 开头是注释,空行忽略。开机时 systemd 的 systemd-modules-load.service 会读取这个目录(以及 /etc/modules)逐个加载。
# 验证配置是否正确、能否加载
sudo systemctl restart systemd-modules-load.service
systemctl status systemd-modules-load.service
lsmod | grep -E 'bonding|ip_vs'
journalctl -u systemd-modules-load.service -b
加载失败时,systemctl status 和 journalctl -u 会给出具体哪个模块失败了、为什么失败。这个目录是排查"开机模块没加载"的首选。
模块参数与黑名单
模块的参数有两种持久化方式:
方式一:modprobe.d 配置文件(推荐,开机即生效)
在 /etc/modprobe.d/ 下放 .conf 文件,语法是 options 模块名 参数=值:
sudo tee /etc/modprobe.d/bonding.conf <<'EOF'
options bonding mode=802.3ad miimon=100 lacp_rate=fast
EOF
# 立即生效(先卸载再按参数加载)
sudo modprobe -r bonding
sudo modprobe bonding
cat /sys/class/net/bond0/bonding/mode 2>/dev/null
方式二:内核命令行(早期加载,用于关键模块)
对于要在很早阶段加载的模块(比如根文件系统所在的控制器驱动),可以写进 GRUB 内核命令行:
# /etc/default/grub
GRUB_CMDLINE_LINUX="... bonding.mode=802.3ad"
# 改完重新生成 grub 配置(详见启动过程那篇)
sudo grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL 系
sudo update-grub # Debian 系
黑名单:阻止模块自动加载
某些模块会被自动加载(比如系统自动探测硬件时),但你不希望它加载——典型场景是网卡有两个驱动,想让系统用你指定的那个。用 blacklist:
sudo tee /etc/modprobe.d/blacklist-example.conf <<'EOF'
# 屏蔽某个模块的自动加载
blacklist nouveau
blacklist floppy
# blacklist 只是阻止自动加载,并没有彻底禁止手动加载
# 要彻底禁止,再加 install 指令
install nouveau /bin/false
EOF
blacklist 和 install ... /bin/false 的区别:blacklist 只阻止内核在探测硬件时自动加载,用户手动 modprobe 仍能加载;install 模块名 /bin/false 则让任何加载请求都执行一个"假"安装(即失败),彻底禁用。屏蔽开源显卡驱动换用专有驱动时,常用后者。
修改后建议重建 initramfs,否则早期阶段仍可能加载:
# RHEL / CentOS / Rocky
sudo dracut -f
# Debian / Ubuntu
sudo update-initramfs -u -k all
常见模块速查
| 模块 | 作用 | 何时需要 |
|---|---|---|
bonding |
网卡绑定,做冗余或聚合带宽 | 双网卡高可用、链路聚合 |
8021q |
VLAN 支持 | 需要配置 VLAN 子接口 |
br_netfilter |
让网桥流量经过 iptables | Docker、Kubernetes、网桥防火墙 |
nf_conntrack |
连接跟踪 | iptables 的 state 匹配、NAT |
ip_vs |
IPVS,LVS 四层负载均衡 | LVS、部分 kube-proxy 模式 |
overlay |
OverlayFS 联合文件系统 | Docker、容器镜像 |
nvidia |
NVIDIA GPU 驱动 | AI 训练、GPU 计算 |
zram |
内存压缩块设备 | 用内存做压缩 Swap |
vfat/ntfs |
文件系统驱动 | 挂载 U 盘、Windows 分区 |
举例,容器环境常需要这些内核模块并配好相关内核参数:
# 加载容器常用的模块
sudo modprobe overlay
sudo modprobe br_netfilter
# 写入开机自动加载
sudo tee /etc/modules-load.d/k8s.conf <<'EOF'
overlay
br_netfilter
EOF
# 配合必要的内核参数
sudo tee /etc/sysctl.d/99-k8s.conf <<'EOF'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
故障排查:加载和卸载报错怎么办
这是本文最实用的部分,几种高频错误逐一拆解。
报错 "Module xxx not found"
模块文件不存在或不在标准路径。排查:
# 确认模块名拼写,用 modinfo 查
modinfo xxx
# 在整个模块目录里搜
find /lib/modules/$(uname -r) -name 'xxx*'
# 查模块是否属于某个包(CentOS/RHEL)
dnf provides '*/xxx.ko*'
常见原因:模块被拆到了单独的包(如 kmod-*、*-modules-extra),需要先安装;或者内核版本和模块不匹配。
报错 "Unknown symbol in module" 或 "Invalid module format"
通常是用 insmod 加载了一个依赖没满足的模块,或者模块是为另一个内核版本编译的。
# 用 modprobe 代替 insmod(自动处理依赖)
sudo modprobe xxx
# 看内核日志里的具体符号错误
dmesg | tail -n 30
如果是第三方模块(如 nvidia、dkms 模块),内核升级后需要重新编译,通常用 DKMS 自动处理:
# RHEL / CentOS / Rocky
sudo dnf install -y dkms
# Debian / Ubuntu
sudo apt install -y dkms
dkms status
sudo dkms autoinstall
报错 "Module xxx is in use"
卸载时最常见,表示有进程或其他模块正在用它。看谁在占用:
lsmod | grep xxx
# Used by 列显示了引用者和次数
modinfo xxx | grep -A1 depends
# 找哪些进程打开了该模块相关的设备
sudo fuser -v /dev/相关设备
sudo lsof | grep 模块名
处理思路:先停掉使用它的进程或服务,再卸载。比如要卸载 nvidia,得先停掉所有用 GPU 的进程;要卸载网络模块,得先关掉用它的网卡。可以用 rmmod -f 强制卸载,但风险极高,可能导致内核崩溃或数据损坏,生产环境不要用。
# 危险操作,仅限明确知道后果的调试场景
sudo rmmod -f xxx # 不推荐
如果是被 initramfs 或内核命令行加载的,还要从对应配置里移除,否则重启又回来。
模块加载后功能没生效
比如 nf_conntrack 加载了但 iptables 的 state 匹配还是报错。检查相关依赖模块是否都加载、内核参数是否配套:
lsmod | grep -E 'nf_conntrack|nf_nat'
sudo sysctl -a | grep conntrack
# 检查 hash 表大小是否够用,不够会丢连接
cat /proc/sys/net/netfilter/nf_conntrack_max
cat /proc/sys/net/netfilter/nf_conntrack_count
nf_conntrack_count 接近 nf_conntrack_max 时,新连接会被丢弃,日志出现 nf_conntrack: table full, dropping packet。要调大 nf_conntrack_max 并配合调大 hash 表。
常见问题
lsmod 输出里模块一大堆,怎么知道哪个是我关心的?
用 grep 过滤:lsmod | grep -i 关键字。想反查某个硬件用的哪个驱动,用 lspci -k 或 ethtool -i,它们会直接告诉你驱动模块名。
如何让模块开机加载,但单位置在哪里?
放在 /etc/modules-load.d/xxx.conf,每行一个模块名。这是 systemd 系统的标准位置,比老的 /etc/modules 更推荐(后者在某些发行版仍支持)。改完 systemctl restart systemd-modules-load.service 验证。
blacklist 之后模块还是被加载了?
blacklist 只阻止内核的自动探测加载,不阻止依赖它的其他模块把它带进来,也不阻止 modprobe 手动加载。要彻底禁止,加 install 模块名 /bin/false,并且更新 initramfs(dracut -f 或 update-initramfs -u),因为早期加载阶段用的是 initramfs 里的模块表。
卸载模块会不会影响正在运行的业务?
会。内核模块通常是给正在使用的硬件或功能提供支持,卸载它可能导致相关功能立即失效,甚至内核崩溃(panic)。除非你明确知道这个模块没有进程在用,否则生产环境不要随便卸载。
内核升级后第三方模块(如 GPU 驱动)失效怎么办?
用 DKMS 管理,它会在内核升级时自动重新编译。装好 dkms 并注册模块后,dkms status 能看到每个模块在各内核下的状态。如果没有 DKMS,升级内核后需要手动重新编译安装驱动。
总结
- 内核模块让内核保持精简又能灵活扩展;日常加载卸载用
modprobe/modprobe -r,insmod/rmmod只在调试时用,因为后者不处理依赖和路径。 lsmod看已加载模块和占用情况,modinfo看模块路径、依赖和参数,depmod -a在新装模块后重建依赖表。- 开机自动加载放
/etc/modules-load.d/,模块参数写/etc/modprobe.d/的options,屏蔽模块用blacklist,要彻底禁用加install 模块名 /bin/false并重建 initramfs。 - 卸载报"in use"说明有进程或其他模块在用,先停服务再卸,别用
rmmod -f强卸;加载报符号或格式错误,优先改用modprobe并看dmesg。 - 容器、防火墙、负载均衡、GPU 都依赖特定模块,部署前先确认模块已加载并写入开机配置,配好相关内核参数,能用 DKMS 管理的第三方驱动就交给 DKMS。
企业QQ咨询




