最可靠方法是两次采样/proc/stat中cpu行的累计ticks差值比:总时间=user+nice+system+idle+iowait+irq+softirq+steal,活跃时间=总时间−idle−iowait,间隔≥200ms。

Linux 下用 /proc/stat 计算总 CPU 使用率最可靠
直接读 /proc/stat 是 Linux 上获取系统级 CPU 负载比例(用户态 + 内核态 + 中断等)的通用做法,不依赖第三方库,也不需要 root 权限。关键不是“当前瞬时值”,而是两次采样间的差值比——因为 /proc/stat 里记录的是自启动以来的累计 ticks。
常见错误是只读一次就除以固定分母,结果毫无意义;或者误把 cpu 行当成单核数据(它其实是所有 CPU 的总和)。
- 每次读取时解析
cpu开头的第一行(注意不是cpu0),提取前 4~8 个数字:user、nice、system、idle、iowait、irq、softirq、steal - 总时间 =
user+nice+system+idle+iowait+irq+softirq+steal - 活跃时间 = 总时间 −
idle−iowait(是否含iowait取决于你定义的“负载”:严格意义的 CPU 执行时间不含iowait,但很多监控工具包含) - 两次采样间隔建议 ≥ 200ms,太短会导致精度崩坏(尤其在空闲系统上
idle可能不变)
Windows 下用 GetSystemTimes 替代 /proc/stat
Windows 没有 /proc/stat,但 GetSystemTimes 提供了等价的内核/用户/空闲时间累计值(单位为 100ns)。它返回三个 FILETIME,需转成 64 位整数再做差值计算。
容易踩的坑:一是忘记调用前检查函数是否存在(Win XP+ 支持,但部分精简版系统可能阉割);二是直接拿 FILETIME 结构体相减出错——必须用 ULARGE_INTEGER 或 std::bit_cast(C++20)转成 uint64_t。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 调用前确认
GetSystemTimes地址有效(可用GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetSystemTimes")) - 三次时间之和才是“总时间”,其中
IdleTime是真正未被使用的周期,其余为活跃时间 - 若函数返回
FALSE(如权限不足或系统不支持),只能 fallback 到GetProcessTimes+ 当前进程占比估算,但那不是系统级负载
C++ 跨平台封装要注意时序与类型安全
写一个跨平台函数时,别用 std::chrono::steady_clock::now() 做采样间隔基准——它和 /proc/stat 或 GetSystemTimes 的时间基不同,应统一用两次系统 API 调用之间的真实耗时(比如记录第二次调用前的时间戳,减去第一次调用后的时间戳)。
另一个关键点是整数溢出:/proc/stat 的数值可达 1e14 级别,FILETIME 更是 1e17,必须全程用 uint64_t 存储差值,避免 int 或 long 在 32 位环境截断。
- Linux 版本解析
/proc/stat推荐用std::istringstream或std::from_chars(C++17),不用atoi——后者对超大数会返回INT_MAX - Windows 版本中,
IdleTime等字段必须通过ULARGE_INTEGER转换:ULARGE_INTEGER{}.QuadPart = ft.dwLowDateTime | (static_cast<uint64_t>(ft.dwHighDateTime) </uint64_t> - 不要在单次调用中返回百分比浮点数——保留原始差值,由上层决定是否归一化,方便调试和避免重复计算
为什么不用 top 或 psutil 的 C++ 绑定
像 psutil 的 C++ 封装(如 psutil-cpp)底层仍是调 /proc/stat 或 GetSystemTimes,多一层抽象反而增加不可控因素:版本兼容性、构建依赖、异常路径处理不一致。而 top 是交互式工具,其输出格式不稳定(比如字段顺序随 locale 变),不能直接 popen("top -bn1") 解析。
更隐蔽的问题是采样策略:top 默认 3 秒刷新,且内部做了平滑处理;直接读系统接口则完全可控——你可以每 500ms 采一次做滚动平均,也可以每 5s 采一次避开抖动。
- 避免用
std::system或popen调外部命令,fork 开销大,且无法保证命令存在(容器环境常删掉top) -
libstatgrab等老牌库已多年未维护,sysinfo在 musl 系统上行为不一致,不如自己读/proc/stat - 如果你只需要“当前大概负载”,且能接受 1~2% 误差,其实两次采样间隔设为 1s,用
sleep_for(1s)就够了;别为了“零延迟”把间隔压到 50ms,那只会放大噪声
iowait 算不算?steal(虚拟机被宿主抢占的时间)要不要计入?这些选择直接影响业务判断,得和运维或 SRE 对齐口径,而不是代码里随便加减几个字段。

















