搬运并适配自国外社区真实案例。原文出处见文末。
现象
Ubuntu 24.04 生产服务器,跑 Java 应用 + PostgreSQL + Nginx,挂载了 3 个 CIFS 网络共享做备份和文件交换。
每运行约一个月,系统开始出现以下症状:
free -h显示 MemAvailable 还有 29GB(总共 48GB),看起来内存充裕- 但 syslog 疯狂刷
systemd-journald: Under memory pressure, flushing caches. drop_caches只能释放约 200MB,完全不符合预期- 服务器逐渐失去响应,SSH 连不上,最终只能强制重启
最诡异的地方:重启后一切正常,meminfo 各项指标也对得上。但跑一段时间后,问题必现。
排查过程
第一回合:常规检查------全部正常
c
$ free -h
total used free shared buff/cache available
Mem: 47Gi 17Gi 1.5Gi 15Mi 28Gi 29Gi
available 有 29G,看起来不像内存不足。查看 /proc/meminfo 关键字段:
bash
MemTotal: 49285376 kB
MemFree: 1611852 kB
MemAvailable: 29837696 kB # 29GB,看起来很多
Buffers: 0 kB
Cached: 367752 kB # ⚠️ 只有 367MB?
Active(file): 7379008 kB # 7GB
Inactive(file): 20216280 kB # 20GB
第二回合:发现矛盾
到这里,有经验的同学应该已经发现问题了。
正常的 Linux 内存统计中,Active(file) + Inactive(file) 约等于 Cached(都是 file-backed pages),差值通常在几百 MB 以内。
但这里:
Active(file) + Inactive(file)≈ 7GB + 20GB = 27GBCached= 367MB
差了将近 27GB! 这 27GB 的 page cache 去哪儿了?
更诡异的是 MemAvailable 依然显示 29GB------这个值理论上应该是 MemFree + 大部分 file pages + 可回收 slab。
第三回合:深入内核统计
查 /proc/vmstat 中 nr_file_pages:
bash
$ grep nr_file_pages /proc/vmstat
nr_file_pages 91938 # 91938 pages × 4KB ≈ 367MB
这和 Cached 的 367MB 对得上。但 Active(file) + Inactive(file) 的 27GB 是哪里来的?
结论 :内核的 page cache 记账出了问题。Cached 字段的计算公式为:
c
Cached = nr_file_pages - SwapCached - Buffers
(源码:fs/proc/meminfo.c)
nr_file_pages 没有被正确更新。内核在回收这些页面时,没有减少 Active(file)/Inactive(file) 的 LRU 计数,导致了"幽灵内存"。
第四回合:定位根因
进一步排查发现,这个 VM 上挂载了 3 个 CIFS 共享来做自动备份:
bash
$ cat /proc/self/mountinfo | grep cifs
//x/x /mnt/y cifs rw,vers=3.1.1,cache=strict,...
//y/y /mnt/x cifs rw,vers=3.1.1,cache=strict,...
//y/x /home/backups/x cifs rw,vers=3.1.1,cache=strict,...
而 buddyinfo 和 pagetypeinfo 显示严重的内存碎片化(大量小块可用,但缺乏连续大块):
bash
Node 0, zone Normal 424 22383 233 # Unmovable / Movable / Reclaimable 块数
碎片化本身不是根因,而是内存泄漏的次生灾害。
根因:CIFS 内核模块的 Page Cache 泄漏
经过 Ubuntu 社区和内核开发者的追踪,最终定位到问题:
Linux 内核 6.8.0-32 中引入的一个 CVE 安全修复补丁,意外导致了 CIFS 客户端的 page cache 泄漏。
具体来说:每次在 CIFS 挂载点上关闭一个文件时,内核 CIFS 模块会泄漏 "lease key" 对象关联的 page cache 页面。这些页面虽然不再被引用,但:
nr_file_pages计数器没有被更新(所以Cached显示不变)- LRU 链表的
Active(file)/Inactive(file)计数也没有被减少(所以看起来还有 27GB) - 内存实际已经泄漏,无法被回收利用
影响的版本
- 引入版本:kernel 6.8.0-32(Ubuntu 24.04 默认内核线的某个更新)
- 修复版本:kernel 6.8.0-64
- 影响范围:Ubuntu 24.04 LTS 全线(generic/azure/aws 等 flavor),任何使用 CIFS 挂载的系统
泄漏速度
根据社区实测:
- 使用
cache=strict(默认):最快,每天可能泄漏数 GB - 使用
cache=loose:泄漏略慢,但依然会触发 OOM(甚至在 45% 内存占用时就触发) - 高频文件操作(如备份脚本中反复 write→close→delete)泄漏最快
解决方案
方案一:升级内核(根本解决)
bash
# 确认当前内核版本
uname -r
# 如果 < 6.8.0-64,升级
sudo apt update
sudo apt install linux-image-generic linux-modules-extra-generic
# 如果稳定版仓库还没到 6.8.0-64,可以用 -proposed
sudo apt install linux-image-generic-proposed linux-modules-extra-generic-proposed
sudo reboot
重启后验证:
bash
uname -r
# 应显示 6.8.0-64-generic 或更高
方案二:回退到安全内核(临时处理)
如果当前环境不能立即升级(如没有重启窗口),回退到已知安全版本:
bash
sudo apt install linux-image-6.8.0-31-generic linux-modules-6.8.0-31-generic
sudo apt-mark hold linux-image-generic linux-headers-generic
sudo reboot
等 6.8.0-64 进入稳定仓库后,再解除 hold 升级。
方案三:挂载选项 workaround(不推荐生产)
如果实在无法换内核,可以临时用 cache=none 挂载:
bash
mount -o remount,cache=none /mnt/y
代价巨大:CIFS 吞吐量下降约 3.5 倍(实测 mongodump 从 1 小时变成 3.5 小时)。
方案四:加 swap 缓解碎片(辅助手段)
即使修复了泄漏,内存碎片化问题可能遗留。建议添加小量 swap(256-512MB)来缓解碎片:
bash
fallocate -l 512M /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
如果担心关键应用被 swap out,在 systemd service 中设置:
ini
[Service]
MemoryMin=4G
如何自检你是否中招
如果你的 Ubuntu 24.04 服务器使用了 CIFS 挂载,跑一下这个一行的 awk:
bash
awk '/MemAvailable:|MemFree:|Buffers:|Cached:/ {a[$1]=$2} END {print a["MemAvailable:"] - (a["MemFree:"] + a["Buffers:"] + a["Cached:"])}' /proc/meminfo
如果这个值持续增长(每天增加几百 MB 到几 GB),你就中招了。
更精确的检测------看 Active(file)+Inactive(file) 和 Cached 的差值:
bash
awk '/^Active\(file\):|^Inactive\(file\):|^Cached:/ {a[$1]=$2}
END {diff = a["Active(file):"] + a["Inactive(file):"] - a["Cached:"];
printf "Active(file)+Inactive(file) = %d kB\nCached = %d kB\n差值 = %d kB (%.1f GB)\n",
a["Active(file):"] + a["Inactive(file):"], a["Cached:"], diff, diff/1048576}' /proc/meminfo
正常系统的差值应该在几百 MB 以内。如果你的差值高达十几 GB,恭喜,你遇到了这个 bug。
启示
MemAvailable不是绝对可靠的。它是内核根据当前状态估算的"可分配"内存,但当内核自身的记账机制出错时,这个数字会严重失真。- 不要只看 top/free 的表面数字 。当 MemAvailable 显示充裕但系统行为反常时,深挖
/proc/meminfo的明细字段,交叉验证。 - CVE 安全修复也可能引入新 bug。这个泄漏正是由某个 CVE 修复引入的。升级前做好回归测试。
- CIFS 在 Linux 上的 page cache 行为与本地文件系统不同。遇到 CIFS 相关问题时,首先怀疑缓存层。
原始出处:
- ServerFault: System under memory pressure with high MemAvail (answered by AlexD, Greg Askew)
- Ubuntu Discourse: File operations on top of a CIFS mount leak memory
- Ubuntu Discourse: Same issue - confirmed fix in kernel 6.8.0-64
本文首发于 CSDN 专栏《运维漏洞指南》,专注于搬运和适配国外社区已验证的生产级运维排障方案。欢迎关注。