/proc/[pid]/statm第二列不准,因含共享库页;精确私有RSS需累加/proc/[pid]/smaps中所有Private_*字段(KB);容器中须用宿主机PID访问smaps。

Linux 下用 /proc/[pid]/statm 读 RSS 不准?别信它
直接读 /proc/[pid]/statm 第二列看似是 RSS(单位为页),但它包含共享库的物理页——比如多个进程共用 glibc,这部分会被重复计入,根本不是「私有」物理内存。你看到的数值通常比实际私有驻留高 20%–50%,尤其在容器或微服务场景下偏差更明显。
真正反映进程独占物理内存的是 /proc/[pid]/smaps 中所有 Private_* 字段之和(单位 KB)。关键字段包括:Private_Clean、Private_Dirty、Private_Hugetlb(若启用大页)。
- 用
std::ifstream打开/proc/[pid]/smaps,逐行匹配以Private_开头的行 - 跳过注释行(以
#开头)和空行 - 每行格式为
Private_Clean: 1234 kB,需用std::sscanf或字符串切分提取数值 - 累计所有匹配项,结果即为精确私有 RSS(KB)
用 libprocps 简化 smaps 解析但要注意版本陷阱
libprocps(v3.3.12+)提供了 proc_get_smaps() 接口,能自动解析并填充 struct proc_smaps,其中 priv_clean、priv_dirty 等字段已汇总好。但它在旧版系统(如 CentOS 7 默认的 procps-ng 3.3.10)中不暴露这些字段,调用会返回 0。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 编译时加
-lprocps,运行时检查procps_version()返回值是否 ≥ 33120 - 若版本不足,降级走手动解析
/proc/[pid]/smaps路径,不要 fallback 到statm - 注意:该库非线程安全,多线程监控时需对每个
proc_t*实例独立调用proc_open()/proc_close()
高频采样时 /proc/[pid]/smaps 的性能代价远超预期
每次读取 smaps 需内核遍历进程全部 VMA(虚拟内存区域),平均耗时 30–200 μs(取决于 mmap 区域数量)。若你的监控程序每 100ms 采集一次,且目标进程有 500+ 个 VMA(常见于 Qt/Java 应用),CPU 时间可能被吃掉 1%–3%。
缓解策略:
- 避免轮询所有进程:只监控指定
pid,不用readdir("/proc")全扫 - 采样间隔 ≥ 500ms;若需更高频,改用
perf_event_open()+PERF_COUNT_SW_PAGE_FAULTS间接估算,但精度下降 - 缓存上次解析结果,仅当
/proc/[pid]/stat中stime/utime变化才重读smaps(适用于低活跃度进程)
容器环境里 getpid() 拿到的 pid 是宿主机视角还是 namespace 视角?
在容器中调用 getpid() 返回的是容器内 PID namespace 的 1 号进程(即你自己的进程),但 /proc/[pid]/smaps 路径必须用宿主机全局 PID。否则会报错 No such file or directory。
正确做法:
- 读
/proc/self/status,提取NSpid:行第二列(即宿主机 PID) - 或更可靠:用
readlink("/proc/self/ns/pid")获取 namespace ID,再查/proc/[host_pid]/status中NSpid是否匹配 - Docker/K8s 场景下,可直接从
/proc/1/status拿宿主机 PID(因容器 init 进程在宿主机 PID namespace 中可见)
没处理好 namespace 映射,监控服务在容器里跑着跑着就静默失败——连错误日志都难定位,因为 open() 返回 ENOENT,而你还在 debug 权限问题。


















