Linux内核与模块管理:uname、lsmod、modprobe、sysctl
内核是 Linux 的心脏,但日常打交道最多的其实是两件事:看版本(uname)、管理模块和参数(lsmod、modprobe、sysctl)。装网卡驱动、开 IP 转发、调文件句柄上限,都落在这几条命令上。这一篇把它们串起来。
一、uname:看内核与系统信息
语法格式
bash
uname [选项]
常用参数
| 参数 | 说明 |
|---|---|
-a |
全部信息:内核名、主机名、版本、架构 |
-r |
只看内核版本号 |
-m |
机器硬件架构(x86_64、aarch64) |
-n |
主机名 |
-v |
内核编译日期 |
示例与预期输出
bash
uname -a
预期输出:
text
Linux web01 6.8.0-45-generic #45~22.04.1-Ubuntu SMP Wed Sep 4 13:02:00 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
拆一下 6.8.0-45-generic:主版本 6、次版本 8、修订 0、发行版补丁 45、generic 是 Ubuntu 的通用内核变体。这个版本号后面找模块路径时天天用。
bash
uname -r
预期输出:
text
6.8.0-45-generic
模块都装在 /lib/modules/$(uname -r)/ 下面。升级内核后旧模块路径还在但不会再被加载,uname -r 一变,整套模块树就换了。
二、lsmod:看已经加载的模块
Linux 内核把驱动做成可插拔模块,需要时装进来,不需要时卸掉。lsmod 列出当前已经在内核里的模块。
语法格式
bash
lsmod
它本质是读 /proc/modules,没有选项。
示例与预期输出
bash
lsmod
预期输出(节选):
text
Module Size Used by
e1000e 413696 0
ixgbe 307200 0
xt_conntrack 16384 1
nf_conntrack 155648 1 xt_conntrack
sd_mod 57344 3
usbcore 311296 4 xhci_hcd,usb_storage
三列含义:模块名、占用内存大小、被多少其他模块/进程引用。最后一列是引用它的模块。Used by 那列不是 0,说明还有东西在用,这时候想卸载会失败------后面讲 modprobe -r 时细说。
想知道某个驱动对应哪个模块,用 modinfo:
bash
modinfo e1000e
预期输出(节选):
text
filename: /lib/modules/6.8.0-45-generic/kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko
version: 6.8.0
license: GPL
description: Intel(R) PRO/1000 Network Driver
author: Intel Corporation
alias: pci:v00008086d000015A3sv*sd*bc*sc*i*
filename 告诉你模块文件在硬盘哪。alias 是 PCI 设备 ID 匹配规则,内核发现对应硬件时自动按 alias 找到这个模块。
三、modprobe:加载与卸载模块
语法格式
bash
modprobe [选项] 模块名
不带选项就是加载。modprobe 比 insmod 强在自动处理依赖:它读 /lib/modules/$(uname -r)/modules.dep,把这个模块依赖的其他模块一起拉进来。
常用参数
| 参数 | 说明 |
|---|---|
| (无) | 加载模块 |
-r |
卸载模块(同时卸载没人再用的依赖) |
-n |
干跑,不真执行,看会发生什么 |
-v |
显示详细动作 |
示例与预期输出
加载一个模块:
bash
sudo modprobe e1000e
没报错就是成了。验证:
bash
lsmod | grep e1000e
预期输出:
text
e1000e 413696 0
卸载:
bash
sudo modprobe -r e1000e
如果这张网卡还在用、有 IP 地址,内核会拒绝卸载,报 Module e1000e is in use。先把网卡 down 掉再卸。
对比一下 insmod:它只往内核塞指定那一个 .ko 文件,不处理依赖。实际工作几乎都用 modprobe,insmod 留着自己写模块调试时用。
开机自动加载模块
希望每次开机都加载某模块,写进配置:
bash
echo "e1000e" | sudo tee /etc/modules-load.d/network.conf
/etc/modules-load.d/ 下任意 .conf 文件里一行一个模块名,开机时 systemd-modules-load.service 会读。Debian 老版本用 /etc/modules,新发行版统一走 modules-load.d。
给模块传参数
有些模块需要启动参数,比如某个声卡要指定缓冲区大小。在 /etc/modprobe.d/ 下写:
text
options snd_hda_intel index=0
文件名随便,.conf 后缀。modprobe snd_hda_intel 时会自动带上 index=0。
四、sysctl:运行时改内核参数
内核暴露了几千个可调参数,绝大多数挂在 /proc/sys/ 下。sysctl 是官方接口。
语法格式
bash
sysctl [选项] [参数=值]
常用参数
| 命令 | 说明 |
|---|---|
sysctl -a |
列出所有可调参数和当前值 |
sysctl 键名 |
查单个参数 |
sysctl -w 键名=值 |
运行时立即改 |
sysctl -p |
从配置文件重新加载 |
sysctl --system |
加载所有默认配置目录 |
示例与预期输出
查 IP 转发开没开:
bash
sysctl net.ipv4.ip_forward
预期输出:
text
net.ipv4.ip_forward = 0
0 是没开。当路由器、跑 Docker、做 k8s 节点都得开成 1。
临时开(重启失效):
bash
sudo sysctl -w net.ipv4.ip_forward=1
预期输出:
text
net.ipv4.ip_forward = 1
永久生效写配置。Ubuntu 上建议写到 /etc/sysctl.d/99-custom.conf,别直接改 /etc/sysctl.conf:
text
net.ipv4.ip_forward = 1
vm.swappiness = 10
加载:
bash
sudo sysctl -p /etc/sysctl.d/99-custom.conf
swappiness=10 的意思是内存还够的时候尽量别用 swap,数据库机器常用这个调优。
/proc/sys/ 路径和 sysctl 键名是一一对应的,把点换斜杠、去掉前缀:
| sysctl 键名 | 对应文件 |
|---|---|
net.ipv4.ip_forward |
/proc/sys/net/ipv4/ip_forward |
vm.swappiness |
/proc/sys/vm/swappiness |
kernel.hostname |
/proc/sys/kernel/hostname |
不想用 sysctl,直接 echo 1 > /proc/sys/net/ipv4/ip_forward 效果一样,重启都失效。
几个高频调参项,搞网络和数据库的人常打交道:
| 键名 | 默认 | 生产常用 | 用途 |
|---|---|---|---|
net.ipv4.ip_forward |
0 | 1 | 开启内核转发,Docker/路由器必开 |
vm.swappiness |
60 | 10 | 越小越倾向用物理内存,数据库建议调低 |
fs.file-max |
随内存 | 655350 | 系统级最大文件句柄数 |
net.core.somaxconn |
4096 | 65535 | TCP 半连接队列上限,高并发 web 调大 |
net.ipv4.tcp_tw_reuse |
0 | 1 | 允许 TIME_WAIT 套接字复用,短连接多的服务有用 |
注意区分两个句柄限制:fs.file-max 是全系统上限,ulimit -n 是单进程上限。调了前者不调后者,业务进程照样报 too many open files。
五、⚠️ 常见错误
modprobe: module xxx not found:多半是内核版本对不上。装的是另一个内核的头文件/模块包,uname -r看当前跑的内核,模块得装到那个版本的目录里。- 卸载模块报
in use:lsmod最后一列看谁在用,把使用者停掉再卸。网卡先ip link set eth0 down,存储先卸载文件系统。 - 改 sysctl 重启失效 :
sysctl -w是临时的。永久配置必须写/etc/sysctl.d/*.conf或/etc/sysctl.conf,再sysctl -p。 - 升级内核后起不来旧模块 :第三方编译的内核模块(比如某些显卡、虚拟化驱动)要针对新内核重新编译,
dkms包能自动做这件事。 - 改
/etc/sysctl.conf不生效 :某些发行版读/etc/sysctl.d/优先,或需要sysctl --system重新加载全部。
六、知识扩展:modules.dep 与 depmod
modprobe 怎么知道一个模块依赖谁?答案是 /lib/modules/$(uname -r)/modules.dep 这个文件。它里面记着每个 .ko 模块的完整依赖树。
手动往 /lib/modules/ 扔了一个新编译的 .ko,modprobe 一开始不认识它,得跑:
bash
sudo depmod -a
depmod 扫描一遍所有 .ko,重新生成 modules.dep。之后 modprobe 才能找到。
系统升级内核、装 dkms 包时,这些步骤都是脚本自动跑的。自己手撸模块的时候忘了 depmod -a,就会出现"文件明明在,modprobe 却说找不到"的怪事。
模块加载失败时,dmesg 比 modprobe 的报错信息详细得多:
bash
dmesg | tail -20
驱动版本不匹配、符号未找到、设备资源冲突,全在内核环形缓冲区里。modprobe 只告诉你"失败了",dmesg 告诉你"为什么"。