即時監控 CPU 使用率是系統監控工具、任務管理器類應用的核心功能。Windows NT 內核系列(NT/2000/XP/Server)與 Windows 9x 系列(95/98/Me)採用完全不同的機制獲取 CPU 使用率。本文詳細解析兩種平台的實現原理,提供可直接編譯運行的 C/C++ 完整代碼。
NT 內核通過 NtQuerySystemInformation 系統呼叫提供三類關鍵資訊:
bKeNumberProcessorsliIdleTime(所有處理器閒置時間總和,100 奈秒單位)liKeSystemTimeCPU 使用率計算公式:
CPU% = 100 - ( (IdleTime_New - IdleTime_Old) / (SystemTime_New - SystemTime_Old) * 100 / ProcessorCount )
typedef struct {
DWORD dwUnknown1;
ULONG uKeMaximumIncrement;
ULONG uPageSize;
ULONG uMmNumberOfPhysicalPages;
ULONG uMmLowestPhysicalPage;
ULONG uMmHighestPhysicalPage;
ULONG uAllocationGranularity;
PVOID pLowestUserAddress;
PVOID pMmHighestUserAddress;
ULONG uKeActiveProcessors;
BYTE bKeNumberProcessors; // 關鍵:處理器邏輯核心數
BYTE bUnknown2;
WORD wUnknown3;
} SYSTEM_BASIC_INFORMATION;
typedef struct {
LARGE_INTEGER liIdleTime; // 系統總閒置時間 (100ns 單位)
DWORD dwSpare[76];
} SYSTEM_PERFORMANCE_INFORMATION;
typedef struct {
LARGE_INTEGER liKeBootTime;
LARGE_INTEGER liKeSystemTime; // 系統總運行時間 (100ns 單位)
LARGE_INTEGER liExpTimeZoneBias;
ULONG uCurrentTimeZoneId;
DWORD dwReserved;
} SYSTEM_TIME_INFORMATION;
#include
#include
#include
#define SystemBasicInformation 0
#define SystemPerformanceInformation 2
#define SystemTimeInformation 3
#define Li2Double(x) ((double)(x).HighPart * 4.294967296E9 + (double)(x).LowPart)
typedef LONG (WINAPI *PFN_NTQUERYSYSTEMINFORMATION)(UINT, PVOID, ULONG, PULONG);
int main(void) {
SYSTEM_BASIC_INFORMATION SysBaseInfo;
SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
SYSTEM_TIME_INFORMATION SysTimeInfo;
PFN_NTQUERYSYSTEMINFORMATION NtQuerySystemInformation =
(PFN_NTQUERYSYSTEMINFORMATION)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
if (!NtQuerySystemInformation) return 1;
// 獲取處理器數量
if (NtQuerySystemInformation(SystemBasicInformation, &SysBaseInfo, sizeof(SysBaseInfo), NULL) != 0)
return 1;
LARGE_INTEGER liOldIdleTime = {0}, liOldSystemTime = {0};
printf("CPU Usage (press any key to exit): ");
while (!_kbhit()) {
// 獲取最新系統時間
if (NtQuerySystemInformation(SystemTimeInformation, &SysTimeInfo, sizeof(SysTimeInfo), NULL) != 0)
break;
// 獲取最新閒置時間
if (NtQuerySystemInformation(SystemPerformanceInformation, &SysPerfInfo, sizeof(SysPerfInfo), NULL) != 0)
break;
if (liOldIdleTime.QuadPart != 0) { // 跳過第一次採樣
double dbIdleTime = Li2Double(SysPerfInfo.liIdleTime) - Li2Double(liOldIdleTime);
double dbSystemTime = Li2Double(SysTimeInfo.liKeSystemTime) - Li2Double(liOldSystemTime);
dbIdleTime = dbIdleTime / dbSystemTime; // 閒置比例
dbIdleTime = 100.0 - dbIdleTime * 100.0 / SysBaseInfo.bKeNumberProcessors + 0.5; // 四捨五入
printf("\b\b\b\b%3d%%", (UINT)dbIdleTime);
}
liOldIdleTime = SysPerfInfo.liIdleTime;
liOldSystemTime = SysTimeInfo.liKeSystemTime;
Sleep(1000); // 1 秒採樣間隔
}
printf("\n");
return 0;
}
9x 系列無 NtQuerySystemInformation,但提供虛擬登錄鍵 HKEY_DYN_DATA 下的效能統計:
PerfStats\StartStat - 啟動計數器(寫入任意值即可)PerfStats\StatData - 讀取當前 CPU 使用率 (KERNEL\CPUUsage,0-100 整數)PerfStats\StopStat - 停止計數器#include
#include
#include
int main(void) {
HKEY hkey;
DWORD dwDataSize, dwType, dwCpuUsage;
// 啟動計數器
if (RegOpenKeyEx(HKEY_DYN_DATA, "PerfStats\\StartStat", 0, KEY_ALL_ACCESS, &hkey) != ERROR_SUCCESS)
return 1;
dwDataSize = sizeof(DWORD);
RegQueryValueEx(hkey, "KERNEL\\CPUUsage", NULL, &dwType, (LPBYTE)&dwCpuUsage, &dwDataSize);
RegCloseKey(hkey);
// 開啟統計資料鍵
if (RegOpenKeyEx(HKEY_DYN_DATA, "PerfStats\\StatData", 0, KEY_READ, &hkey) != ERROR_SUCCESS)
return 1;
printf("CPU Usage (press any key to exit): ");
while (!_kbhit()) {
dwDataSize = sizeof(DWORD);
RegQueryValueEx(hkey, "KERNEL\\CPUUsage", NULL, &dwType, (LPBYTE)&dwCpuUsage, &dwDataSize);
printf("\b\b\b\b%3d%%", dwCpuUsage);
Sleep(500); // 9x 更新頻率較快,可 500ms 採樣
}
printf("\n");
RegCloseKey(hkey);
// 停止計數器
if (RegOpenKeyEx(HKEY_DYN_DATA, "PerfStats\\StopStat", 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS) {
dwDataSize = sizeof(DWORD);
RegQueryValueEx(hkey, "KERNEL\\CPUUsage", NULL, &dwType, (LPBYTE)&dwCpuUsage, &dwDataSize);
RegCloseKey(hkey);
}
return 0;
}
class CpuMonitor {
public:
virtual ~CpuMonitor() {}
virtual int GetUsage() = 0; // 返回 0-100
static CpuMonitor* Create() {
OSVERSIONINFO osvi = { sizeof(osvi) };
GetVersionEx(&osvi);
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
return new NtCpuMonitor();
else
return new Win9xCpuMonitor();
}
};
NtQuerySystemInformation 為非文檔化 API,雖極其穩定但微軟不保證長期相容。現代開發建議使用 PDH (Performance Data Helper) 或 WMI (Win32_Processor.LoadPercentage)HKEY_DYN_DATA 僅存在於 9x,NT 系統無此鍵bKeNumberProcessors)