Windows中计算进程CPU占用率需用GetProcessTimes获取进程用户态与内核态时间差,再用GetSystemTimes获取系统忙时差(kernel+user−idle),公式为:cpu_pct = (process_time_delta_ms / system_busy_time_delta_ms) × 100,结果可超100%(多核满载时正常)。

用 GetSystemTimes 和 GetProcessTimes 算 CPU 占用率,不是直接读注册表或 WMI
Windows 上没有现成的“进程 CPU 百分比” API,GetProcessTimes 返回的是内核态 + 用户态的总时间(100ns 单位),但这个值本身没意义——必须和系统总时间差值做归一化。关键不是“怎么取时间”,而是“怎么算比率”。系统总时间得靠 GetSystemTimes 拿到空闲、内核、用户三段时钟,其中 kernel + user - idle 才是系统实际忙的时间总量。
常见错误是只用 GetProcessTimes 两次相减,然后除以采样间隔(比如 1 秒),这完全错误:进程时间单位是 CPU 时间,不是墙钟;1 秒内若进程只跑了 10ms,它 CPU 利用率就是 1%,不是 100%。
实操要点:
-
GetSystemTimes必须在每次采样时都调用,不能只初始化一次;系统空闲时间是累计值,差值才反映真实忙时 - 两次
GetProcessTimes的差值要转成毫秒(除以 10000),再和系统忙时差值(同样单位)做比值 - 如果进程刚启动或已退出,
GetProcessTimes可能返回 0 或失败,需检查GetLastError() - 最小采样间隔建议 ≥ 250ms;太短会导致分子为 0 或抖动剧烈,尤其低负载时
计算公式必须用“系统忙时差值”作分母,不是“采样间隔”
CPU 利用率本质是:该进程占用的 CPU 时间 ÷ 同期内系统所有 CPU 核心的总可用时间。假设双核,1 秒内系统忙时共 1800ms(即两核合计跑了 1800ms),某进程用了 360ms,那它占比就是 20%。
立即学习“C++免费学习笔记(深入)”;
所以正确公式是:
double cpu_pct = (process_time_delta_ms / system_busy_time_delta_ms) * 100.0;
其中:
-
process_time_delta_ms = (user2 + kernel2) - (user1 + kernel1),两次GetProcessTimes的用户+内核时间差(单位 100ns → 除以 10000 得 ms) -
system_busy_time_delta_ms = [(kernel2 + user2 - idle2) - (kernel1 + user1 - idle1)] / 10000,GetSystemTimes返回的三值都要参与运算 - 结果可能 >100%(多线程跑满多核),这是正常现象;若超 100% 且你只开单线程,说明有其他线程共享同一进程 ID(如 DLL 创建的线程)
获取目标进程句柄时,OpenProcess 权限不能漏掉 PROCESS_QUERY_LIMITED_INFORMATION
Windows Vista 之后,默认权限不足以读取进程时间信息。PROCESS_QUERY_INFORMATION 在低完整性进程(如 IE、Edge 沙箱、UAC 提权后降权的程序)下会失败,必须用 PROCESS_QUERY_LIMITED_INFORMATION —— 它不要求调试权限,且能读 GetProcessTimes 所需数据。
典型错误写法:OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid),运行时 GetLastError() 返回 5(拒绝访问)。
正确写法:
HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (!hProc) {
// 处理失败,可能是权限不足或进程已退出
}注意:PROCESS_QUERY_LIMITED_INFORMATION 是最低要求,不需要 SE_DEBUG_NAME 特权,普通用户进程也能用。
多核系统下百分比可能超过 100%,这不是 bug,是设计如此
很多工具(如任务管理器)默认显示“相对单核”的百分比(上限 100%),而你用上述公式算出来的是“绝对 CPU 时间占比”,在 N 核机器上理论最大值是 N × 100%。比如四核全满,你的进程跑了全部时间,结果就是 400%。
要不要归一化?取决于用途:
- 监控告警:用绝对值更合理,400% 明确表示占满四核
- UI 展示(类似任务管理器):可除以逻辑核心数,再截断到 100%,但需提前调用
GetSystemInfo()拿dwNumberOfProcessors - 性能分析:保留原始值,避免掩盖多线程扩展性问题
别硬凑成 0–100% 范围——这会把“四核各跑 50%”和“单核跑 200%”混淆成同一个数字。
最易被忽略的一点:GetSystemTimes 在某些旧版 Windows(如 Win7 SP1 之前)可能返回空闲时间为 0,导致分母为 0;务必在计算前检查 system_busy_time_delta_ms > 0,否则跳过本次更新或返回 -1 表示无效。


















