Windows需用PDH API通过两次采样\Process()\% Processor Time计数器计算单核百分比;Linux需解析/proc/[pid]/stat中utime/stime等字段,结合clock_gettime求CPU时间占比;二者语义不同,不可直接统一为0–100%。

Windows下用PDH API获取进程CPU使用率
Windows没有直接返回“当前进程CPU占用率”的函数,必须通过性能计数器(PDH)查询 \Process(。注意:这个值是**相对于单个逻辑核心**的百分比,且需两次采样计算差值——瞬时读取毫无意义。
关键步骤:
- 调用
PdhOpenQuery创建查询句柄 - 用
PdhAddCounter添加计数器,进程名需动态获取(推荐用GetProcessId+GetProcessImageFileName或更稳妥的EnumProcessModules+GetModuleBaseName) - 第一次调用
PdhCollectQueryData后等待 ≥100ms(否则第二次采样差值恒为0) - 第二次调用
PdhCollectQueryData,再用PdhGetFormattedCounterValue提取double类型的百分比值
常见错误:PdhAddCounter 传入硬编码进程名(如 "notepad")会导致多实例冲突;未等待就连续采集,结果始终为0或极大异常值。
Linux下读取/proc/[pid]/stat计算CPU时间占比
Linux不提供现成的“CPU占用率”接口,得自己算:用 clock_gettime(CLOCK_MONOTONIC) 记录时间间隔,同时解析 /proc/ 中第14、15(utime、stime)、第16、17(cutime、cstime)字段,求和得到总CPU时间(单位:clock ticks),再除以系统 sysconf(_SC_CLK_TCK) 换算为秒,最后除以经过的真实时间。
立即学习“C++免费学习笔记(深入)”;
注意点:
-
/proc/[pid]/stat的字段顺序固定但易错数,第1个字段是pid,第14个才是utime——建议用空格分割后按索引取,别靠肉眼数 - 必须用同一时刻的两个样本:先读一次
/proc/self/stat和clock_gettime,sleep 200ms以上,再读第二次 - 如果进程刚启动,初始utime/stime可能为0,首次差值偏高,建议丢弃第一次结果
示例片段:std::ifstream f("/proc/self/stat"); std::vector<:string> v; /* split line */; long utime = std::stol(v[13]);
跨平台封装要注意的陷阱
别试图写一个统一接口返回“0–100%”——Windows PDH返回的是归一化到单核的值(可能超过100%,比如双核满载时显示198%),而Linux计算的是占所有逻辑核心总时间的比例(理论最大值=100% × 核心数)。两者语义不同,强行统一会误导监控逻辑。
更实际的做法:
- Windows路径下保留原始PDH值,标注为“per-core %”
- Linux路径下计算出“total CPU %”,即
(delta_cpu_time / delta_wall_time) * 100.0 - 若需对比,统一换算成“等效单核利用率”:Linux结果除以
sysconf(_SC_NPROCESSORS_ONLN)
另一个坑:std::this_thread::sleep_for 在高负载下实际休眠时间可能严重不足,导致采样间隔不准——改用 clock_nanosleep(Linux)或 WaitForSingleObject(Windows)更可靠。
为什么GetProcessTimes在Windows上不推荐
GetProcessTimes 返回的是总内核态+用户态时间,但它无法告诉你这段时间里系统总共运行了多久。没有分母,就无法算出“占用率”。有人尝试用两次 GetProcessTimes 差值除以 GetTickCount64 差值,这是错的——GetTickCount64 是墙钟时间,而CPU时间增长速度取决于实际调度,二者非线性相关。尤其在进程被抢占、挂起时,墙钟前进但CPU时间停滞,结果会严重失真。
唯一能用 GetProcessTimes 的场景是估算“自启动以来累计CPU耗时”,而非实时占用率。真要监测,PDH是Windows下唯一靠谱方案。











