MemAvailable 还有 29GB,系统却报内存压力?——Ubuntu 24.04 CIFS 内核 Page Cache 泄漏排查实录

人工智能Agent 2026-07-04 69
预计阅读时间:9 分钟

搬运并适配自国外社区真实案例。原文出处见文末。

现象

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 各项指标也对得上。但跑一段时间后,问题必现。

排查过程

第一回合:常规检查——全部正常

$ free -h
              total        used        free      shared  buff/cache   available
Mem:           47Gi        17Gi       1.5Gi        15Mi        28Gi        29Gi

available 有 29G,看起来不像内存不足。查看 /proc/meminfo 关键字段:

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 = 27GB - Cached = 367MB 差了将近 27GB! 这 27GB 的 page cache 去哪儿了? 更诡异的是 MemAvailable 依然显示 29GB——这个值理论上应该是 MemFree + 大部分 file pages + 可回收 slab

第三回合:深入内核统计

/proc/vmstatnr_file_pages

$ grep nr_file_pages /proc/vmstat
nr_file_pages 91938    # 91938 pages × 4KB ≈ 367MB

这和 Cached 的 367MB 对得上。但 Active(file) + Inactive(file) 的 27GB 是哪里来的? 结论:内核的 page cache 记账出了问题。Cached 字段的计算公式为:

Cached = nr_file_pages - SwapCached - Buffers

(源码:fs/proc/meminfo.cnr_file_pages 没有被正确更新。内核在回收这些页面时,没有减少 Active(file)/Inactive(file) 的 LRU 计数,导致了"幽灵内存"。

第四回合:定位根因

进一步排查发现,这个 VM 上挂载了 3 个 CIFS 共享来做自动备份:

$ 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,...

buddyinfopagetypeinfo 显示严重的内存碎片化(大量小块可用,但缺乏连续大块):

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)泄漏最快

解决方案

方案一:升级内核(根本解决)

# 确认当前内核版本
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

重启后验证:

uname -r
# 应显示 6.8.0-64-generic 或更高

方案二:回退到安全内核(临时处理)

如果当前环境不能立即升级(如没有重启窗口),回退到已知安全版本:

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 挂载:

mount -o remount,cache=none /mnt/y

代价巨大:CIFS 吞吐量下降约 3.5 倍(实测 mongodump 从 1 小时变成 3.5 小时)。

方案四:加 swap 缓解碎片(辅助手段)

即使修复了泄漏,内存碎片化问题可能遗留。建议添加小量 swap(256-512MB)来缓解碎片:

fallocate -l 512M /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

如果担心关键应用被 swap out,在 systemd service 中设置:

[Service]
MemoryMin=4G

如何自检你是否中招

如果你的 Ubuntu 24.04 服务器使用了 CIFS 挂载,跑一下这个一行的 awk:

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 的差值:

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。

启示

  1. MemAvailable 不是绝对可靠的。它是内核根据当前状态估算的"可分配"内存,但当内核自身的记账机制出错时,这个数字会严重失真。
  2. 不要只看 top/free 的表面数字。当 MemAvailable 显示充裕但系统行为反常时,深挖 /proc/meminfo 的明细字段,交叉验证。
  3. CVE 安全修复也可能引入新 bug。这个泄漏正是由某个 CVE 修复引入的。升级前做好回归测试。
  4. 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 专栏《运维漏洞指南》,专注于搬运和适配国外社区已验证的生产级运维排障方案。欢迎关注。


本文由 admin 原创,转载请注明出处。

相关推荐

评论

0
暂无评论,来发表第一条评论吧

发表评论

登录 后发表评论

发现更多