如何通过PID计算每个进程的CPU占用百分比?附现有实现代码
公式正确性验证
你给出的公式是错误的,逻辑混淆了系统空闲时间和进程时间的统计维度,计算结果会是整机CPU占用率而非单个进程的CPU占用率。
正确的单进程CPU占用率计算逻辑如下:
单进程CPU占用率 = (统计间隔内进程内核时间增量 + 进程用户时间增量) / (统计间隔内系统总时间增量) * 100%
系统总时间增量 = 统计间隔时长 * 逻辑CPU核心数 * 单位时间时钟滴答数,也可以通过(系统内核时间增量 + 系统用户时间增量)直接计算得到
现有代码问题梳理
- 全局静态变量存储上一次时间,遍历多进程时会互相覆盖,每个进程需要独立存储自己的上一次内核、用户时间戳
OpenProcess申请PROCESS_ALL_ACCESS权限过高,大部分系统进程、权限更高的进程会拒绝访问,仅需要申请PROCESS_QUERY_LIMITED_INFORMATION权限即可满足读取进程时间的需求,大幅降低打开失败概率- 遍历每个进程时单独Sleep 2.5秒的逻辑完全不可用,若系统中存在上百个进程,单次遍历耗时会达到数百秒,正确逻辑是批量采集所有进程的第一次时间,统一等待间隔后批量采集第二次时间统一计算
- 函数入参逻辑不匹配,你定义的
CalculateCPULoad仅接收两个参数,调用时却传入了三个参数,逻辑完全不对应
修正后代码示例
#include <windows.h> #include <tlhelp32.h> #include <map> #include <iostream> #include <tchar.h> // FILETIME转64位整数 static ULONGLONG FileTimeToInt64(const FILETIME& ft) { return (((ULONGLONG)(ft.dwHighDateTime)) << 32) | ((ULONGLONG)ft.dwLowDateTime); } // 获取系统逻辑核心数 int GetLogicalCpuCount() { SYSTEM_INFO sysInfo = {0}; GetSystemInfo(&sysInfo); return sysInfo.dwNumberOfProcessors; } int main() { const int CALC_INTERVAL = 1000; // 统计间隔1秒,可自行调整 int cpuCount = GetLogicalCpuCount(); std::map<DWORD, std::pair<ULONGLONG, ULONGLONG>> lastProcessTime; // 存储每个PID的上一次内核、用户时间 ULONGLONG lastSysKernel = 0, lastSysUser = 0; while (true) { // 循环刷新,若不需要持续刷新可去掉外层循环 // 第一次采集:获取当前系统时间 FILETIME sysIdle, sysKernel, sysUser; GetSystemTimes(&sysIdle, &sysKernel, &sysUser); ULONGLONG curSysKernel = FileTimeToInt64(sysKernel); ULONGLONG curSysUser = FileTimeToInt64(sysUser); ULONGLONG sysDelta = (curSysKernel + curSysUser) - (lastSysKernel + lastSysUser); // 采集所有进程的当前时间 HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnap == INVALID_HANDLE_VALUE) break; PROCESSENTRY32 pe = {0}; pe.dwSize = sizeof(PROCESSENTRY32); if (Process32First(hSnap, &pe)) { do { HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID); if (hProcess) { FILETIME ftCreate, ftExit, ftKernel, ftUser; if (GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { ULONGLONG curKernel = FileTimeToInt64(ftKernel); ULONGLONG curUser = FileTimeToInt64(ftUser); // 不是第一次采集则计算占用率 if (lastSysKernel != 0 && lastProcessTime.count(pe.th32ProcessID)) { auto& lastTime = lastProcessTime[pe.th32ProcessID]; ULONGLONG procDelta = (curKernel - lastTime.first) + (curUser - lastTime.second); float cpuUsage = 0.0f; if (sysDelta > 0) { cpuUsage = (procDelta * 100.0f) / sysDelta; // 如果要显示和任务管理器一致的单核心占比,不需要除以cpuCount // 如果要显示占整机的比例,加上:cpuUsage /= cpuCount; } _tprintf(TEXT("进程名:%s \t PID:%u \t CPU占用:%.2f%%\n"), pe.szExeFile, pe.th32ProcessID, cpuUsage); } // 更新存储的进程时间 lastProcessTime[pe.th32ProcessID] = {curKernel, curUser}; } CloseHandle(hProcess); } } while (Process32Next(hSnap, &pe)); } CloseHandle(hSnap); // 更新系统时间 lastSysKernel = curSysKernel; lastSysUser = curSysUser; // 等待间隔,GUI程序可替换为定时器避免阻塞 Sleep(CALC_INTERVAL); system("cls"); // 清屏刷新,可根据需求调整 } return 0; }
Sleep优化方案
- 若为GUI程序,可将进程统计逻辑放到独立工作线程,用
SetTimer触发定时采集,避免阻塞主线程导致界面卡顿 - 不需要高精度统计的场景下,1-2秒的间隔已经足够贴合系统任务管理器的显示效果,不需要过短的间隔增加系统负载
内容的提问来源于stack exchange,提问作者PraysforWind
相关产品推荐
相关产品推荐

