
Linux服务器内存被‘吃’光了手把手教你用/proc/meminfo和slabinfo定位内核内存泄露凌晨三点服务器告警铃声突然响起。监控系统显示某台核心业务服务器的可用内存正以每小时2%的速度持续下降距离触发OOM Killer只剩不到6小时。作为值班运维你必须在早高峰前解决这个内存黑洞。本文将带你像侦探破案一样通过/proc/meminfo和/proc/slabinfo这两个现场勘查工具快速锁定内核内存泄露的元凶。1. 紧急诊断用户态还是内核态泄露当服务器出现内存缓慢耗尽时首先要确定泄露发生在用户态还是内核态。这就像医生区分内科还是外科问题——治疗方法完全不同。1.1 查看内存全景图/proc/meminfo登录问题服务器运行以下命令获取内存快照cat /proc/meminfo | grep -E MemTotal|MemFree|Slab|SReclaimable|SUnreclaim典型的内核泄露场景会显示如下特征MemTotal: 32817152 kB MemFree: 1024000 kB # 持续减少 Slab: 8388608 kB # 异常偏高 SReclaimable: 524288 kB SUnreclaim: 7864320 kB # 不可回收内存占比极高关键指标解读指标正常范围泄露特征Slab5%总内存20%总内存SUnreclaim/Slab30%-50%80%MemFree下降曲线阶梯式持续平滑下降注意建议同时保存free -m输出作为辅助参考但/proc/meminfo的Slab数据更精确1.2 时间轴对比分析内存问题诊断必须引入时间维度。建议按以下步骤操作创建监控时间点watch -n 300 date %F %T mem.log; \ cat /proc/meminfo | grep -E MemFree|Slab|SUnreclaim mem.log两小时后停止监控分析变化趋势awk /Slab/{print $2} mem.log | graph -w 100 -h 10如果Slab值呈线性增长而MemFree同步减少基本可判定是内核态泄露。2. 锁定嫌犯Slab缓存分析确认内核泄露后下一步是找出具体哪个Slab缓存类型在偷内存。2.1 获取Slab详细清单cat /proc/slabinfo | awk NR1; $31024 {print} | sort -k2 -nr这个命令会保留表头信息过滤对象大小1KB的缓存小对象泄露影响有限按活跃对象数降序排列重点观察列active_objs正在使用的对象数量objsize单个对象大小字节objperslab每个Slab页能存放的对象数2.2 动态监控增长趋势对可疑的Slab类型进行持续监控watch -n 60 date %T; \ awk /kmalloc-8192/{print \$2,\$3} /proc/slabinfo如果发现某个缓存的active_objs持续增加而num_objs不变就是典型的内存泄露特征。常见泄露大户kmalloc-*通用内存缓存dentry目录项缓存buffer_head文件系统缓存skbuff_head_cache网络数据包缓存3. 深度取证Slab调试技巧对于确认泄露的Slab缓存需要进一步取证分析。3.1 启用Slab跟踪无需编译内核# 设置跟踪标记 echo 1 /sys/kernel/slab/leaking_slab/trace # 查看分配调用栈 cat /sys/kernel/slab/leaking_slab/alloc_calls # 查看释放调用栈 cat /sys/kernel/slab/leaking_slab/free_calls3.2 解读调用栈信息假设我们发现kmalloc-8192的分配调用栈显示__alloc_skb0x98/0x238 age430/366961/410069 pid0-1467 cpus1,3 pskb_expand_head0xa0/0x2b0 age22161/203241/396156 pid0-1467 cpus1这表示内存主要被网络栈的__alloc_skb函数申请申请频率较高age值跨度大涉及多CPU核心cpus1,3提示如果free_calls中对应函数调用次数明显少于alloc_calls就是典型的泄露证据4. 应急处理与根治方案4.1 临时缓解措施如果暂时无法重启服务可以尝试手动回收Slab缓存echo 2 /proc/sys/vm/drop_caches限制特定Slab增长echo limit slab_name 1000 /proc/slabinfo4.2 长期解决方案根据调用栈分析结果通常需要修复内核模块的内存释放逻辑更新有问题的驱动版本对第三方内核模块进行压力测试典型修复案例网络驱动未正确释放DMA缓冲区文件系统模块的inode缓存引用计数错误自定义内核模块的kmalloc/kfree不匹配5. 高级调试技巧对于复杂场景可能需要更深入的调试手段5.1 使用SystemTap监控stap -e probe kernel.function(kmem_cache_alloc).return { if (execname() your_process) { printf(%s %d\n, probefunc(), $bytes) } }5.2 内存泄露模式分析不同类型泄露的特征对比泄露类型Slab特征调用栈特点常见场景单次大分配单个大对象深调用链驱动初始化持续小泄露对象数增长浅调用链中断处理循环泄露周期性增长重复栈定时任务5.3 自动化监控脚本建议部署以下脚本到核心服务器#!/bin/bash SLAB_LIMIT$(( $(grep MemTotal /proc/meminfo | awk {print $2}) * 20 / 100 )) while true; do CURRENT_SLAB$(grep Slab /proc/meminfo | awk {print $2}) [ $CURRENT_SLAB -gt $SLAB_LIMIT ] \ alert Slab usage exceeded 20% of total memory sleep 300 done记得去年处理过一台数据库服务器dentry缓存泄露导致每天凌晨准时OOM。最终发现是某个监控工具频繁扫描/proc目录却不关闭文件描述符。这种案例教会我内存问题往往隐藏在看似无关的角落。