6.18 Linux 内核管理——/proc、/sys、模块与 udev
预计阅读时间:17 分钟
📖 目录
学习目标
学完本章,你将能够:
- 从
/proc读取 CPU、内存、负载、进程和网络统计信息 - 通过
/sys查看和修改设备参数(I/O 调度器、网络属性、电源状态) - 使用
lsmod、modprobe、rmmod管理内核模块及/etc/modprobe.d/配置 - 编写 udev 规则固定设备名称、设置权限、触发自定义动作
- 用
sysctl做运行时内核参数调优并持久化到配置文件 - 完成 Ubuntu/Debian 内核的升级、回退与清理
核心知识
内核管理分为六个子领域:
| 子领域 | 用户空间接口 | 主要命令/文件 |
|---|---|---|
| 进程/系统信息 | /proc | /proc/meminfo, /proc/loadavg, /proc/cpuinfo |
| 设备模型 | /sys | /sys/class/net, /sys/block |
| 内核模块 | lsmod / modprobe | /etc/modprobe.d/*.conf, /lib/modules/ |
| 设备管理 | udev | /etc/udev/rules.d/, udevadm |
| 运行时调优 | sysctl | /etc/sysctl.conf, /proc/sys/ |
| 内核本体 | 包管理器 | apt install linux-image-*, update-grub |
知识关联
- 前置知识:2.3:系统信息与性能查看 系统信息与性能查看(/proc 基础)、1.5:文件系统结构 Linux 文件系统结构(设备文件概念)、2.6:硬件信息查看 系统启动流程(内核引导与 GRUB)
- 后续影响:5.12:eBPF 基础 eBPF 基础(内核可编程进阶)、4.6:Linux 性能调优 Linux 性能调优(内核参数优化与性能分析)
- 配套技术:/proc + /sys + sysctl 构成内核信息获取与调优三角,udev 规则管理设备持久化命名,modprobe 处理模块依赖
原理讲解
为什么 Linux 要用 /proc 和 /sys 两个虚拟文件系统
/proc 和 /sys 的存在源于 Unix 哲学"一切皆文件"——内核通过虚拟文件系统向用户空间暴露内部状态和控制接口。/proc(procfs)诞生于早期 Linux,主要暴露进程信息和系统统计(如 /proc/meminfo、/proc/[pid]/status),以及可调内核参数(/proc/sys/)。/sys(sysfs)诞生于 2.6 内核,专门呈现 Linux 设备模型的层次结构——总线、设备、驱动、类都在这里有对应的目录。之所以需要两个文件系统,是因为它们的设计目标不同:/proc 是扁平的键值对集合,适合快速查看系统状态;/sys 是结构化的目录树,完整表达设备拓扑关系。/proc/sys/ 的参数可以通过 sysctl 命令修改,而 /sys/ 下的参数通常通过 echo 直接写入。
内核模块为什么要区分 modprobe 和 insmod
modprobe 和 insmod 的区别源于模块依赖管理的复杂性。Linux 内核模块之间存在依赖关系(如 nft_tables 依赖 nf_tables),手动处理依赖链既繁琐又容易出错。insmod 只能加载单个 .ko 文件,不解析依赖,适合调试场景。modprobe 依据 /lib/modules/$(uname -r)/modules.dep(由 depmod 生成)自动解析依赖并递归加载所有前置模块。同理,modprobe -r 递归卸载整条依赖链,比 rmmod 更安全。这种设计让模块管理从"人工追踪依赖"变成"自动处理",大幅降低了运维复杂度。
sysctl 参数为什么要区分运行时和持久化
sysctl 参数的"运行时修改"和"持久化"分离设计是为了平衡安全性和便利性。运行时修改(sysctl -w 或写 /proc/sys/)立即生效,适合排障和测试——你可以快速验证某个参数对性能的影响,重启后自动恢复默认值。持久化(写入 /etc/sysctl.d/*.conf)在每次启动时加载,适合生产环境的固定调优。这种"先测试再持久"的工作流降低了调优风险——如果某个参数导致系统不稳定,重启即可恢复。注意 sysctl -p 只加载文件里声明的项,不会清空其他运行时修改,这避免了配置文件之间的冲突。
/proc 是一个伪文件系统(procfs),由内核动态生成,不占用磁盘空间。它暴露了进程信息(/proc/[pid]/)、系统统计(meminfo、loadavg、uptime)、网络状态(/proc/net/)以及可调内核参数(/proc/sys/)。写 /proc/sys/ 下的文件等效于 sysctl -w,修改即时生效但重启丢失。
/sys 是 sysfs 文件系统的挂载点,呈现 Linux 设备模型的层次结构——总线、设备、驱动、类都在这里有对应的目录。相比 /proc 的扁平键值对,/sys 用目录树完整表达设备拓扑。
内核模块(.ko 文件)存放在 /lib/modules/$(uname -r)/。模块可以内建(built-in,编译时嵌入内核镜像)或可加载(loadable)。modprobe 自动处理模块依赖,优先于 insmod / rmmod。/etc/modprobe.d/ 用于设置模块参数、加载顺序和黑名单。
udev 基于 netlink 监听内核 uevent,根据 /etc/udev/rules.d/ 中的规则匹配设备属性(vendor、model、路径等),执行命名、权限设置或触发脚本。规则匹配成功后,udevadm trigger 可强制重新应用。
sysctl 接口对应 /proc/sys/ 下的参数树,可通过 sysctl -w 运行时修改,写入 /etc/sysctl.conf 或 /etc/sysctl.d/*.conf 实现持久化。
内核版本号规则:uname -r 输出如 6.8.0-49-generic,拆解为:主版本 6、次版本 8、补丁版本 0、发行版补丁 49(Ubuntu/Debian 额外维护)、风味后缀 -generic(另有 -lowlatency、-aws、-azure 等)。2.6 时代曾用次版本奇偶区分稳定/开发线(奇数=开发版),3.0 之后内核不再遵循该规则。
内核版本类型选择:
| 类型 | 维护周期 | 示例 | 适用场景 |
|---|---|---|---|
| LTS(长期支持) | 约 6 年,期间持续发布小版本修复 | 6.6.x、6.12.x | 生产服务器、嵌入式设备 |
| Mainline(主线) | 每 2-3 个月一个大版本,维护窗口短 | 6.13、6.14 | 尝鲜新特性、新硬件支持 |
| 发行版定制(如 Ubuntu -generic) | 跟随发行版生命周期(5-10 年) | 6.8.0-49-generic | 绝大多数桌面与服务器用户 |
企业发行版(Ubuntu LTS、RHEL)基于某个 LTS 内核持续回填安全补丁,普通用户跟随发行版仓库升级即可,无需手动追主线。
内核参数生效验证:sysctl -a 可导出全部参数,sysctl net.ipv4.ip_forward 查询单项;注意 sysctl -p 文件 只加载文件里声明的项,不会清空其他运行时修改。判断持久化是否成功:修改后重启,再查询同一参数确认值仍在。
示例代码
# /proc 系统监控
cat /proc/meminfo | head -5
# 输出: MemTotal: 16310348 kB
# 输出: MemFree: 8245672 kB
cat /proc/loadavg
# 输出: 0.12 0.08 0.05 1/456 12345
# /proc/pid 进程信息
cat /proc/$$/status
# /sys 查看和设置 I/O 调度器
cat /sys/block/sda/queue/scheduler
# 输出: [mq-deadline] kyber none
echo none > /sys/block/sda/queue/scheduler
# 模块管理
lsmod | head -5
# 输出: Module Size Used by
# 输出: nf_tables 245760 2
modinfo nf_tables
# 输出: filename: /lib/modules/6.8.0-49-generic/kernel/net/netfilter/nf_tables.ko
# 输出: description: Netfilter nf_tables subsystem
modprobe -v xfs
# 输出: insmod /lib/modules/6.8.0-49-generic/kernel/fs/xfs/xfs.ko
# udev 规则: 固定 USB 网卡名为 eth-custom
# /etc/udev/rules.d/70-persistent-net.rules
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="00:11:22:33:44:55", NAME="eth-custom"
# 重新加载生效
udevadm control --reload-rules && udevadm trigger
# sysctl 调优常用集合
sysctl -w net.core.somaxconn=65535
# 输出: net.core.somaxconn = 65535
sysctl -w fs.file-max=2097152
# 输出: fs.file-max = 2097152
sysctl -w vm.swappiness=10
# 输出: vm.swappiness = 10
sysctl -p
# 输出: net.core.somaxconn = 65535
# 输出: fs.file-max = 2097152
# 输出: vm.swappiness = 10
# 内核升级完整流程
uname -r
# 输出: 6.8.0-48-generic
apt update && apt install -y linux-image-6.8.0-49-generic
# 输出: ... Unpacking linux-image-6.8.0-49-generic ... Setting up ...
grep menuentry /boot/grub/grub.cfg | head -5
# 输出: menuentry 'Ubuntu' --class ubuntu ... $menuentry_id_option 'gnulinux-simple-xxx'
# 输出: menuentry 'Ubuntu, with Linux 6.8.0-49-generic' ...
# 输出: menuentry 'Ubuntu, with Linux 6.8.0-48-generic' ...
update-grub
# 输出: Sourcing file /etc/default/grub
# 输出: Generating grub configuration file ... Found linux image: /boot/vmlinuz-6.8.0-49-generic
reboot
sysctl 常用参数分类表:
| 分类 | 参数 | 作用 |
|---|---|---|
| 网络 | net.ipv4.tcp_syncookies | SYN Flood 防护(生产默认 1) |
net.ipv4.ip_local_port_range | 客户端可用端口范围,高并发出站连接需调大 | |
net.ipv4.tcp_fin_timeout | FIN-WAIT-2 等待秒数,缩短可释放连接槽 | |
| 内存 | vm.swappiness | swap 使用倾向(0-100),服务器常用 10 |
vm.dirty_ratio / vm.dirty_background_ratio | 脏页回写阈值,影响磁盘写入突发与 IO 卡顿 | |
| 文件系统 | fs.file-max | 系统级最大文件句柄数(默认约 2^20) |
| 内核 | kernel.core_pattern | core dump 文件命名与存放路径 |
kernel.pid_max | 最大 PID 号,容器化/高进程数场景需调大 |
高性能服务器参数集合:
# /etc/sysctl.d/99-performance.conf
# TCP 缓冲区(BDP = 带宽 × 延迟,高吞吐链路需放大)
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# 连接队列与 TIME_WAIT 复用
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
# 文件句柄上限
fs.file-max = 2097152
# OOM 行为:0 = 内核按评分杀进程(默认);2 = 触发 OOM 时不杀,直接 panic
vm.panic_on_oom = 0
kernel.panic = 10 # 内核 panic 后 10 秒自动重启,减少停机
# 立即生效
sysctl -p /etc/sysctl.d/99-performance.conf
模块加载管理:
# 开机自动加载模块:/etc/modules-load.d/*.conf
echo "wireguard" > /etc/modules-load.d/wireguard.conf
systemctl restart systemd-modules-load.service
lsmod | grep wireguard
# 模块参数:/etc/modprobe.d/*.conf
# 语法:options 模块名 参数名=值
options bonding miimon=100 mode=active-backup
# 黑名单(驱动冲突、硬件误导时禁用):
# /etc/modprobe.d/blacklist-bluetooth.conf
blacklist btusb
modprobe 与 insmod 的区别:modprobe 依据 /lib/modules/$(uname -r)/modules.dep(由 depmod 生成)自动解析依赖并递归加载;insmod 只能加载单个 .ko 文件、不解析依赖,仅适合调试场景。modprobe -r 递归卸载整条依赖链,比 rmmod 更安全。
# 查看模块依赖与元信息
modinfo -d bonding
depmod -a # 安装新模块/内核后重建依赖索引
modprobe -n -v bonding # 预演加载(-n = dry run),不真正执行
模块加载链:开机时 systemd 读取 /etc/modules-load.d/ 自动加载模块,/etc/modprobe.d/ 控制加载参数与黑名单,同名项按文件名字典序后加载覆盖先加载。内核发现新硬件时(udev uevent)也会按需自动加载对应驱动——这就是插入 USB 设备驱动"立即可用"的原因。
GRUB 默认内核与回滚:
# 查看已安装内核与当前使用
dpkg --list | grep linux-image
uname -r
# 指定默认启动项(/etc/default/grub)
GRUB_DEFAULT=1 # 按菜单序号(0 为第一项)
# 更稳妥的用法:始终启动"上次选中的内核"
GRUB_DEFAULT=saved
GRUB_SAVEDEFAULT=true
update-grub
# 仅下一次启动用指定项,用于验证新内核后再回滚
grub-set-default 1
grub-reboot 1
reboot
# 回滚后清理故障内核
apt remove --purge linux-image-6.8.0-49-generic
apt autoremove && update-grub
RHEL / Rocky 系内核升级(dnf):
dnf check-update kernel # 查看可用更新
dnf update -y kernel # 升级内核(新内核作为追加引导项,不覆盖旧版)
dnf list installed kernel # 列出已安装内核
# 查看所有引导项序号与内核对应关系
grubby --info=ALL | grep -E "^index|kernel"
grub2-set-default 0 # 设置默认引导项
grub2-mkconfig -o /etc/grub2.cfg # RHEL 系重建配置(UEFI 系统为 /boot/efi/.../grub.cfg)
reboot
RHEL 系默认保留已安装的全部内核版本,回滚只需 grub2-set-default 对应序号 后重启;清理旧内核用 dnf remove kernel-旧版本号(保留最近两个为宜)。
启动参数(内核命令行)
内核命令行参数写在 /etc/default/grub 的 GRUB_CMDLINE_LINUX 中,update-grub 重建引导项后生效。常用参数:
| 参数 | 用途 |
|---|---|
quiet / splash | 隐藏启动日志 / 显示开机画面(排障时去掉) |
nomodeset | 禁用显卡模式设置,解决黑屏、花屏 |
noapic / nolapic | 禁用 APIC,老硬件中断异常时的兜底方案 |
nokaslr | 关闭内核地址空间随机化,便于内核调试(生产勿用) |
systemd.unit=multi-user.target | 跳过图形界面直达命令行,图形崩溃时急救 |
init=/bin/bash | 用 bash 替代 init,密码重置等紧急修复(注意手动挂载根分区) |
rdbreak | 在 initramfs 阶段暂停,排查根文件系统挂载失败 |
ip=dhcp | initramfs 阶段配置网络(网络引导、NFS 根) |
# /etc/default/grub 示例
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX="net.ipv4.tcp_congestion_control=bbr"
update-grub
# 查看当前实际生效的参数
cat /proc/cmdline
# 输出: BOOT_IMAGE=/boot/vmlinuz-6.8.0-49-generic root=UUID=xxx ro quiet splash
# 临时修改:GRUB 菜单按 e 编辑引导项,在 linux 行追加参数后 Ctrl+X 启动,
# 重启即失效——排障时比改配置文件更安全
查看启动日志:dmesg 与 journalctl -k -b 都能读取内核环形缓冲,用于确认启动参数是否生效、驱动是否加载失败:dmesg | grep -i error、dmesg | grep -i bbr。
紧急修复实例(忘记 root 密码):GRUB 菜单按 e 编辑引导项,在 linux 行末尾追加 init=/bin/bash 后 Ctrl+X 启动。进入单用户 bash 后先执行 mount -o remount,rw / 把根分区改为可写,再 passwd root 重置密码,最后 exec /sbin/init 恢复正常引导。
内核编译简介
绝大多数场景使用发行版内核即可;编译内核适合需要定制驱动、启用安全特性或体验最新功能的高级场景。完整流程(视硬件 30 分钟 - 2 小时):
# 1. 获取源码(以 kernel.org 主线为例)
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.1.tar.xz
tar xf linux-6.12.1.tar.xz && cd linux-6.12.1
# 2. 以当前内核配置为起点(最省事)
cp /boot/config-$(uname -r) .config
make olddefconfig
# 3. 图形化调整选项
make menuconfig
# General setup → Local version 给内核打自定义后缀
# Device Drivers → Network device support → WireGuard
# File systems → 勾选需要的文件系统(ext4 / xfs / btrfs)
# 4. 编译安装
make -j$(nproc) # 按核数并行
make modules_install # 模块装入 /lib/modules/6.12.1-custom/
make install # 生成 /boot/vmlinuz + initramfs 并更新 GRUB
# 5. 验证
uname -r
# 输出: 6.12.1-custom
注意事项:编译前先 apt build-dep linux 安装依赖(gcc、make、flex、bison、libncurses-dev);驱动优先编译为模块(<M>)而非内建(*),便于后续单独升级;make install 后务必确认 GRUB 菜单保留旧内核可回退;源码与编译产物约需 10 GB 磁盘空间。
常见编译报错:No rule to make target 多为缺依赖(重装 flex / bison / libssl-dev);Killed 是内存不足,调低 -j 并行度或加大 swap;permission denied 检查源码目录权限——不要以 root 解压到 /root 之外再以普通用户编译。
OOM 行为与排查:内存不足时 OOM Killer 按 oom_score 选进程杀死,内核日志形如 Out of memory: Killed process 1234 (java)。对关键进程豁免:echo -17 > /proc/1234/oom_score_adj(-1000 完全豁免,仅 root 可写)。观察历史 OOM 事件:journalctl -k | grep -i "out of memory"。
常见错误
| 现象 | 原因 | 解决 |
|---|---|---|
modprobe: FATAL: Module not found | 模块名拼写错误或内核版本不匹配 | 用 modprobe -l | grep keyword 确认正确名称;检查 uname -r 版本 |
rmmod: ERROR: Module is in use | 有其他模块或进程依赖目标模块 | lsmod | grep module_name 查看 Used by 列;先停止依赖方或 modprobe -r 递归卸载 |
unable to load keyring | SecureBoot 启用且模块未签名 | 进 BIOS 关闭 SecureBoot,或用 mokutil 导入签名 |
| udev 规则不生效 | 规则名优先级或匹配条件有误 | udevadm test 模拟测试;文件名数字越大优先级越低(99-*.rules 最后执行) |
sysctl: permission denied | 非 root 或无权限写入对应参数 | 加 sudo;部分安全参数受 kernel.unprivileged_* 限制 |
| modprobe 报 FATAL 但模块文件存在 | 依赖索引过期或依赖模块缺失 | depmod -a 重建 modules.dep 后重试 |
| 升级内核后第三方驱动(显卡)失效 | dkms 模块未针对新内核重建 | dkms status 查看;安装 dkms 后重装驱动源码包 |
| 新内核启动进入 initramfs 紧急模式 | 根文件系统驱动缺失或 /etc/fstab 错误 | GRUB 高级选项回退旧内核重建 /etc/fstab 或用 live CD 修复 |
| sysctl 重启后参数丢失 | 只用了 sysctl -w 运行时修改,未持久化 | 写入 /etc/sysctl.d/*.conf 并重启后复查 |
| 自编译内核启动后驱动缺失 | 驱动编为内建(*)或未执行 modules_install | 驱动优先用 <M>;依次执行 make modules_install 与 make install |
最佳实践
- 只读优先:除了明确的调优场景,生产环境只在
/proc//sys上做只读操作;写操作前充分理解副作用。 - 模块黑名单:禁用不必要的内核模块(如
pcspkr、floppy),通过/etc/modprobe.d/blacklist-*.conf管理,不要直接编辑/etc/modprobe.conf。 - udev 规则命名:使用
70-/80-/99-前缀控制规则优先级。自定义规则统一放/etc/udev/rules.d/,不动/usr/lib/udev/rules.d/。 - sysctl 分文件:按功能域拆分
/etc/sysctl.d/下的文件(如90-network.conf、90-memory.conf),不要全部塞进/etc/sysctl.conf。 - 内核保留:升级后保留近两个旧内核(
apt-mark hold linux-image-*选定版本),清理前确认 GRUB 菜单可回退。 - 先测试再持久:sysctl 调优参数先用
sysctl -w运行时验证稳定性,再写入配置文件持久化。 - 升级窗口:生产内核升级选业务低谷,升级后先在新内核下跑完整冒烟测试,确认无回归再清理旧内核。
- 记录基线:调优前用
sysctl -a > baseline.txt保存参数基线,升级内核或排查性能回归时便于比对差异。 - 留后路:任何涉及引导层面的变更(内核参数、默认内核)都先确认 GRUB 菜单存在可用的旧项,变更后重启验证再固化。
练习题
- 运行
cat /proc/loadavg,解释三个数值分别代表什么时间间隔的平均负载。 - 用
/proc文件系统找出当前 bash 进程的 PID、PPID 和状态。 - 写一条 udev 规则:当 USB 存储设备
/dev/sdb插入时,自动将其挂载到/mnt/usb并设置权限为0770。 - 用
sysctl查看当前系统的net.ipv4.ip_forward值,开启 IP 转发后使其永久生效。 - 安装一个新内核(
apt install linux-image-generic-hwe-*),重启后用uname -r验证,再回退到旧内核启动。 - 查看已加载的模块中找到
snd相关的所有模块,用modinfo查看其中任意一个模块的描述。 - 通过
/sys/class/net/找到主网卡的 MAC 地址和当前链路速率。
点击查看答案
- 三个字段分别代表过去 1 分钟、5 分钟、15 分钟的平均活跃进程数(R 状态 + D 状态)。值高于 CPU 核数表示过载。
cat /proc/self/status查看自己的进程信息,Pid 当前 PID,PPid 父进程 PID,State 当前状态(R/S/D/Z)。/etc/udev/rules.d/99-usb-automount.rules:ACTION=="add", KERNEL=="sd[b-z]", SUBSYSTEM=="block", RUN+="/usr/bin/mount /dev/%k /mnt/usb"。权限用MODE="0770"。sysctl net.ipv4.ip_forward查看,sysctl -w net.ipv4.ip_forward=1临时开启。/etc/sysctl.conf或/etc/sysctl.d/99-ipforward.conf加net.ipv4.ip_forward=1持久化。- 安装 HWE 内核后
uname -r显示新版本。回退:GRUB 启动菜单选 Advanced → 选旧内核,进入后apt remove linux-image-新版本。 lsmod | grep snd列出 snd_hda_intel, snd_pcm 等。modinfo snd_hda_intel显示描述、作者、依赖、参数等。cat /sys/class/net/eth0/address显示 MAC 地址。cat /sys/class/net/eth0/speed显示链路速率(如 1000 Mbps)。
学习检查点
学完本章后,请检验自己是否掌握以下内容:
| 检查项 | 自测问题 | 验证方法 |
|---|---|---|
| 概念理解 | 能用自己的话解释 Linux 内核的模块化架构和内核参数 | 尝试向他人讲解 |
| 命令操作 | 能不查文档完成内核模块加载卸载、内核参数配置 | 在终端实际执行 |
| 原理掌握 | 能说出内核升级的流程和内核启动参数的作用 | 画出流程图 |
| 故障排查 | 能独立排查内核模块加载失败或新内核无法启动的问题 | 模拟故障并修复 |
| 最佳实践 | 能说明为什么生产环境需要保留旧内核作为回滚选项 | 对比不同方案 |
本章总结
速查表
| 路径/命令 | 用途 |
|---|---|
/proc/loadavg | 查看系统平均负载 |
/proc/[pid]/status | 查看进程状态和资源 |
/sys/class/net/ | 查看网络设备信息 |
lsmod | 列出已加载的内核模块 |
modprobe | 加载/卸载内核模块 |
udevadm | 管理和调试 udev 规则 |
sysctl | 查询和调优内核参数 |
内核管理是 Linux 系统管理员的核心能力,覆盖了从信息采集到运行时调整再到系统升级的全链路。三个虚拟文件系统各有分工:/proc 管进程与系统状态,/sys 管设备拓扑,/proc/sys/+sysctl 管参数调优。内核模块提供了按需扩展能力,udev 则让设备管理自动化、可编程。升级内核时务必保留回退选项,始终遵循"先验证、再持久"的安全原则。
延伸阅读
- 内核文档:
https://www.kernel.org/doc/html/latest/ - udev 手册:
man 7 udev、man 8 udevadm sysctl可调参数完整列表:sysctl -a | less- Linux 内核模块编程指南(The Linux Kernel Module Programming Guide)
- Red Hat 企业版 Linux 性能调优指南(RHEL Performance Tuning Guide)