Windows平台下C程序监控其他进程CPU使用率的实现方法
Windows下用C程序获取任意进程CPU使用率的实现方法
要在Windows下获取任意进程的CPU使用率,核心思路是通过两次时间采样计算差值(单次获取的是累计时间,无法直接得到实时使用率),结合系统级和进程级的时间统计API来实现。以下是具体步骤和代码示例:
核心依赖的Windows API
需要用到这些头文件和库:
- 头文件:
windows.h、psapi.h - 链接库:
psapi.lib、kernel32.lib
实现步骤
枚举系统中所有进程ID
使用EnumProcesses函数获取当前运行的所有进程ID,需注意缓冲区大小——若第一次调用返回的进程数等于缓冲区容量,需扩容后重新调用。打开进程并获取权限
对每个进程ID,调用OpenProcess获取进程句柄,需申请PROCESS_QUERY_INFORMATION和PROCESS_VM_READ权限。部分系统进程可能无法打开,需处理这类错误。两次采样时间数据
- 第一次采样:调用
GetProcessTimes获取进程的用户态/内核态累计时间,同时调用GetSystemTimes获取系统的空闲时间、内核态/用户态总时间。 - 等待一段固定时间(比如1秒),进行第二次采样,获取相同的时间数据。
- 第一次采样:调用
计算CPU使用率
通过两次采样的时间差值,计算进程占用的CPU比例:- 系统总时间差 = (第二次系统用户时间 + 第二次系统内核时间) - (第一次系统用户时间 + 第一次系统内核时间)
- 进程时间差 = (第二次进程用户时间 + 第二次进程内核时间) - (第一次进程用户时间 + 第一次进程内核时间)
- 进程CPU使用率 = (进程时间差.QuadPart * 100.0) / 系统总时间差.QuadPart
注:该结果是进程相对于系统总CPU资源的使用率(比如4核系统中,单核心满负载的进程使用率为25%)。
阈值检测
将计算出的使用率与设定阈值对比,标记超过阈值的进程。
示例代码
#include <windows.h> #include <psapi.h> #include <stdio.h> #include <tchar.h> #include <stdlib.h> // 设定CPU使用率阈值(比如50%) #define CPU_THRESHOLD 50.0 // 存储时间采样数据的结构体 typedef struct { FILETIME process_user; FILETIME process_kernel; FILETIME system_idle; FILETIME system_kernel; FILETIME system_user; } TimeSample; // 采样一次时间数据 BOOL TakeSample(DWORD process_id, TimeSample* sample) { HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id); if (hProcess == NULL) { return FALSE; } // 获取进程时间 if (!GetProcessTimes(hProcess, NULL, NULL, &sample->process_kernel, &sample->process_user)) { CloseHandle(hProcess); return FALSE; } // 获取系统时间 if (!GetSystemTimes(&sample->system_idle, &sample->system_kernel, &sample->system_user)) { CloseHandle(hProcess); return FALSE; } CloseHandle(hProcess); return TRUE; } // 计算CPU使用率 double CalculateCpuUsage(const TimeSample* first, const TimeSample* second) { // 计算系统总时间差 ULONGLONG system_total_first = (ULONGLONG)first->system_user.dwHighDateTime << 32 | first->system_user.dwLowDateTime; system_total_first += (ULONGLONG)first->system_kernel.dwHighDateTime << 32 | first->system_kernel.dwLowDateTime; ULONGLONG system_total_second = (ULONGLONG)second->system_user.dwHighDateTime << 32 | second->system_user.dwLowDateTime; system_total_second += (ULONGLONG)second->system_kernel.dwHighDateTime << 32 | second->system_kernel.dwLowDateTime; ULONGLONG system_diff = system_total_second - system_total_first; if (system_diff == 0) { return 0.0; } // 计算进程时间差 ULONGLONG process_total_first = (ULONGLONG)first->process_user.dwHighDateTime << 32 | first->process_user.dwLowDateTime; process_total_first += (ULONGLONG)first->process_kernel.dwHighDateTime << 32 | first->process_kernel.dwLowDateTime; ULONGLONG process_total_second = (ULONGLONG)second->process_user.dwHighDateTime << 32 | second->process_user.dwLowDateTime; process_total_second += (ULONGLONG)second->process_kernel.dwHighDateTime << 32 | second->process_kernel.dwLowDateTime; ULONGLONG process_diff = process_total_second - process_total_first; return (double)process_diff * 100.0 / system_diff; } int _tmain() { DWORD process_ids[1024]; DWORD bytes_returned; // 枚举所有进程ID if (!EnumProcesses(process_ids, sizeof(process_ids), &bytes_returned)) { _tprintf(_T("EnumProcesses failed. Error: %lu\n"), GetLastError()); return 1; } DWORD process_count = bytes_returned / sizeof(DWORD); // 第一次采样所有进程的时间数据 TimeSample* first_samples = malloc(process_count * sizeof(TimeSample)); if (first_samples == NULL) { _tprintf(_T("Memory allocation failed.\n")); return 1; } for (DWORD i = 0; i < process_count; i++) { TakeSample(process_ids[i], &first_samples[i]); } // 等待1秒,让时间产生差值 Sleep(1000); // 第二次采样并计算使用率 for (DWORD i = 0; i < process_count; i++) { TimeSample second_sample; if (!TakeSample(process_ids[i], &second_sample)) { continue; // 进程已退出或无法访问 } double cpu_usage = CalculateCpuUsage(&first_samples[i], &second_sample); if (cpu_usage >= CPU_THRESHOLD) { // 获取进程名称 TCHAR process_name[MAX_PATH] = _T("<unknown>"); HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_ids[i]); if (hProcess != NULL) { HMODULE hModule; DWORD bytes_needed; if (EnumProcessModules(hProcess, &hModule, sizeof(hModule), &bytes_needed)) { GetModuleBaseName(hProcess, hModule, process_name, sizeof(process_name)/sizeof(TCHAR)); } CloseHandle(hProcess); } _tprintf(_T("Process %s (PID: %lu) CPU usage: %.1f%% (exceeds threshold of %.1f%%)\n"), process_name, process_ids[i], cpu_usage, CPU_THRESHOLD); } } free(first_samples); return 0; }
注意事项
- 权限问题:部分系统进程需要管理员权限才能打开,建议以管理员身份运行程序,避免无法获取部分进程的信息。
- 采样间隔:示例中使用1秒间隔,间隔过短会导致时间差值过小,计算结果误差大;间隔过长则实时性降低,可根据需求调整。
- 进程退出处理:两次采样之间可能有进程退出,第二次
OpenProcess或GetProcessTimes会失败,需跳过这类进程。 - 编译设置:编译时需链接
psapi.lib(比如在VS中添加#pragma comment(lib, "psapi.lib"))。
内容的提问来源于stack exchange,提问作者KemVolt
相关产品推荐
相关产品推荐

