/proc/[pid]/statm第二列RSS不准,实际应读smaps_rollup的Private_KB总和;旧内核需解析smaps并缓存,注意COW、THP虚高及跨平台差异。

Linux 下用 /proc/[pid]/statm 读 RSS 不准?别信它
直接读 /proc/[pid]/statm 第二列得到的“RSS”其实是进程所有物理页总数(含共享库、mmap 共享内存、甚至部分内核页),不是私有物理内存。你看到 120MB,实际私有驻留可能只有 35MB——尤其在多线程或加载大量动态库时偏差极大。
真正反映“这个进程独占、没被其他进程共享”的物理内存,得看 /proc/[pid]/smaps 中每个内存段的 Private_KB 字段总和。这是唯一可靠来源。
- 用
cat /proc/self/smaps | awk '/^Private_KB:/ {sum += $2} END {print sum " KB"}'手动验证:对比statm的第二列,通常差 2–5 倍 -
smaps是实时快照,但解析开销比statm高一个数量级(尤其大进程有上千个 VMA 区域) - 注意:从 Linux 4.15+ 开始,
smaps_rollup提供汇总行,含Private_KB总和,可替代逐行扫描,大幅提速
C++ 实现高效获取私有 RSS:优先用 smaps_rollup
不要自己 fopen + getline 逐行扫 smaps。对生产环境进程(如浏览器、数据库后台),VMA 数量常超 2000,单次解析耗时可达毫秒级,监控频率一高就拖垮性能。
正确做法是读 /proc/[pid]/smaps_rollup(若内核支持),它只有一行 Private_KB:,解析成本几乎为零:
立即学习“C++免费学习笔记(深入)”;
#include <fstream>
#include <string>
#include <sstream>
long get_private_rss_kb(pid_t pid) {
std::ifstream f("/proc/" + std::to_string(pid) + "/smaps_rollup");
if (!f.is_open()) return -1; // fallback to smaps or error
std::string line;
while (std::getline(f, line)) {
if (line.rfind("Private_KB:", 0) == 0) {
std::istringstream iss(line.substr(11));
long kb = 0;
iss >> kb;
return kb;
}
}
return -1;
}
- 若
smaps_rollup不存在(旧内核),再降级到解析完整smaps;但必须加缓存或限频(如 1s 内最多查 1 次) - 注意权限:目标进程需与当前进程同用户,或你有
ptrace权限(否则读/proc/[pid]返回空或 permission denied) - 别用
system("awk ...")调 shell —— fork 开销大、不可控、易被安全策略拦截
监控时 RSS 波动大?关键要看 MMUPageSize 和 MMUPageSize
私有 RSS 不是稳定值。即使进程不做分配,它也会因内核页表映射变化、THP(透明大页)拆分/合并、page cache 回收等剧烈跳变。单纯画曲线容易误判泄漏。
真正值得关注的是:持续增长的私有匿名页(Anonymous + Private_KB),排除文件映射和共享内存干扰:
- 在
smaps每个段中,同时匹配Anonymous:行(非 0)且Private_KB:> 0 的区域,累加其Private_KB - 忽略
Size:大但Private_KB:为 0 的段(如只读代码段、共享库) - 特别警惕
MMUPageSize: 2MB段:THP 启用时,一个 2MB 私有页就算作 2048KB,但实际使用可能只有几 KB——这会造成“虚高”,需结合MMUPageSize和MMUPageSize判断是否真实占用
跨平台?Windows 上没有等价 RSS,别硬套
Windows 没有“私有物理内存”概念。GetProcessMemoryInfo 返回的 WorkingSetSize 是工作集(含共享页),PrivateUsage 是私有字节(虚拟地址空间),不等于物理内存。
若必须跨平台监控,接受现实:Linux 用 smaps_rollup.Private_KB,Windows 只能用 PROCESS_MEMORY_COUNTERS_EX.PrivateUsage 作近似替代(误差常达 3–10 倍),并在文档里明确标注“此值非物理内存,仅用于趋势对比”。
强行统一指标只会掩盖问题。比如一个进程在 Windows 上 PrivateUsage 增长,在 Linux 上 Private_KB 稳定,大概率是它在 Windows 上触发了大量页面提交(commit),而 Linux 下 mmap 默认 lazy-alloc,物理页按需分配——根本不是同一层的事。
最常被忽略的一点:RSS 监控必须绑定具体进程生命周期。fork 出的子进程会复制父进程的 Private_KB 快照,但后续写时复制(COW)才真正产生新物理页。刚 fork 完立刻采样,数值会虚高,需等待至少一次写操作后再统计。


















