最可靠方式是读取/proc/stat并两次采样计算差值:取cpu行前8项和为total,idle+iowait为空闲时间,CPU使用率=(total₂−total₁)−(idle₂+iowait₂−idle₁−iowait₁)/(total₂−total₁)×100%,采样间隔须≥100ms。

Linux下用/proc/stat计算CPU负载百分比最可靠
直接读取/proc/stat是Linux上获取真实CPU使用率的唯一通用方式,其他接口(如getloadavg)返回的是平均负载(runnable + uninterruptible进程数),不是百分比。关键在于采样两次cpu行,用时间差反推忙闲比例。
实操要点:
-
/proc/stat第一行以cpu开头,后面是空格分隔的数值:user、nice、system、idle、iowait、irq、softirq、steal、guest、guest_nice(内核2.6.33+) - 必须至少间隔100ms以上采样两次,否则差值为0导致除零或结果失真
- 真正参与计算的是前8项之和(
total = user + nice + system + idle + iowait + irq + softirq + steal),其中idle和iowait代表空闲时间 - CPU使用率 =
(total2 - total1) - (idle2 + iowait2 - idle1 - iowait1)/(total2 - total1)× 100%
示例片段(C++17):
std::ifstream f("/proc/stat");
std::string line;
std::getline(f, line);
// 解析"cpu 1234 56 789 101112 ..." → 跳过"cpu"后取前10个数字
Windows需用PdhAddCounter + PdhCollectQueryData
Windows没有/proc等伪文件系统,必须走PDH(Performance Data Helper)API,且不能用GetSystemTimes——它只返回内核/用户态总时间,无法剔除空闲线程时间,算出来永远接近100%。
立即学习“C++免费学习笔记(深入)”;
关键约束:
- 计数器路径必须是
\Processor(_Total)\% Processor Time,用_Total而非具体核心编号,否则多核下结果不准 - 首次调用
PdhCollectQueryData后必须等待至少1秒再取值,否则返回PDH_INVALID_DATA - 需要手动处理
PDH_FMT_LONG格式转换,PdhGetFormattedCounterValue返回的是整型毫秒级值,需除以10000.0才得百分比 - 程序需链接
pdh.lib,否则链接失败报LNK2019: unresolved external symbol Pdh*
macOS只能靠host_processor_info拼凑近似值
macOS没有公开的单次CPU使用率API,host_processor_info返回的是各状态时间戳(user、system、idle、nice),但文档明确警告:“这些值仅用于计算相对变化,不保证绝对精度”。也就是说,你只能自己存上一次的值做差分。
踩坑重点:
- 必须用
mach_host_self()获取host port,传错会返回KERN_INVALID_ARGUMENT - 返回的
cpu_load_info_t中cpu_ticks是数组,顺序固定为{user, system, idle, nice},不能硬编码索引名 - 第一次调用结果不可信,至少要丢弃前两次采样(常见现象:首次返回idle为0,导致计算出99%+假高负载)
- 无法获取iowait、irq等细分项,所以结果比Linux略偏高,尤其在磁盘繁忙时
跨平台封装要注意采样周期与线程安全
别试图用std::chrono::steady_clock控制“精确1秒采样”——实际间隔受调度延迟影响,可能偏差±15ms。更稳妥的做法是记录起始时间,循环sleep 100ms并检查是否超时,最后用真实耗时重算百分比分母。
多线程场景下必须注意:
- Linux:每个线程读
/proc/stat没问题,但全局变量存上次采样值必须加锁或用thread_local - Windows:PDH查询句柄(
PDH_HQUERY)不能跨线程共享,否则PdhCollectQueryData随机失败 - macOS:
host_processor_info本身线程安全,但你自己缓存的cpu_ticks旧值必须用原子变量或互斥量保护
最易被忽略的一点:所有系统在休眠唤醒后,/proc/stat或host_processor_info的时间戳都会跳变,导致单次差分结果爆掉(比如显示300%)。必须检测前后两次total差值是否异常大(例如>2秒),触发重新初始化。



















