答案是直接读取/proc/pid/status中VmPgfault和VmPgMajFault字段获取软硬缺页累计次数,再通过两次采样差值除以单调时钟测得的时间间隔计算每秒速率。

Linux下直接读取/proc/pid/status里的VmPgfault和VmPgMajFault
软缺页(minor fault)和硬缺页(major fault)的累计计数就藏在 /proc/[pid]/status 里,字段分别是 VmPgfault(软缺页总数)和 VmPgMajFault(硬缺页总数)。C++ 程序想获取当前进程的实时速率,得自己做两次采样再相减——因为内核不提供“每秒增量”接口,只给累计值。
注意:这两个字段单位是“次数”,不是字节;且仅对当前进程有效,子进程不共享计数。
- 用
getpid()获取当前 pid,拼出路径如/proc/12345/status - 逐行读取文件,匹配以
VmPgfault:和VmPgMajFault:开头的行 - 用
sscanf或std::stoi提取数字,别忘了跳过前导空格和制表符 - 首次读取后 sleep 1 秒再读第二次,差值即为近似每秒中断数
避免因/proc文件不可读导致程序崩溃
如果程序以非特权用户运行,或被 seccomp、容器限制了 procfs 访问,open("/proc/self/status", O_RDONLY) 可能失败,返回 -1 并设 errno 为 EACCES 或 ENOENT。不能假设路径一定可读。
- 每次打开前检查
access("/proc/self/status", R_OK)是否成功 - 读取后立即 close,不要长期持有 fd —— /proc 是虚拟文件系统,fd 占用无实际意义但可能干扰调试
- 若读取失败,返回 -1 或抛异常(取决于你的错误策略),不要 fallback 到猜测值
- 某些容器环境(如 gVisor、Kata Containers)根本不挂载完整 /proc,此时字段可能缺失或恒为 0
用clock_gettime(CLOCK_MONOTONIC)替代sleep保证采样间隔精度
sleep(1) 实际延迟常大于 1 秒(受调度延迟、信号中断影响),导致计算出的“每秒”值偏低。更稳的方式是用单调时钟主动等待满 1 秒。
Linux 安全审计工具,检查 SSH 配置、开放/监听端口、防火墙规则(ufw/iptables/nftables)、失败登录尝试、sudoers 权限...
立即学习“C++免费学习笔记(深入)”;
- 第一次读完后调用
clock_gettime(CLOCK_MONOTONIC, &start) - 循环中不断读当前时间,直到
current.tv_sec - start.tv_sec > 0 || (current.tv_sec == start.tv_sec && current.tv_nsec - start.tv_nsec >= 1e9) - 这样能确保两次采样间隔 ≥1 秒,误差控制在毫秒级
- 注意
tv_nsec可能溢出,建议用timespec_get(C11)或手动做带借位的减法
硬缺页突增往往意味着真实性能瓶颈
软缺页多数是分配新页、COW 后首次写入等常规操作,开销小;而硬缺页必须从磁盘加载页(比如 mmap 的文件页未缓存、堆内存触发 swap),延迟常达毫秒级。观察到 VmPgMajFault 每秒持续 >10 次,基本可以判定 I/O 或内存压力已成瓶颈。
- 配合
cat /proc/[pid]/maps查看哪些内存区域在频繁缺页(如大块 mmap 文件、匿名映射) - 用
pmap -x [pid]观察 RSS 和 PSS,确认是否真因物理内存不足引发 swap - 注意:JVM、Python 解释器等运行时会主动预分配大量虚拟内存,导致
VmPgfault高但实际无害;重点盯VmPgMajFault
真正难的是区分缺页来源——是代码 malloc 太多?mmap 了冷数据?还是系统全局内存紧张?光看两个数字不够,得结合 /proc/[pid]/smaps 里的 MMUPageSize、MMUPageSize 和 Swap 字段交叉验证。

















