首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Windows 取得 CPU 使用率完整指南:NT 核心查詢與 9x 登錄檔方案

     編輯:關於C++

前言:沒有標準 API 的系統指標

Windows 沒有提供單一 GetCpuUsage() API。取得 CPU 使用率需根據作業系統版本採用不同機制:

  • Windows NT 系列 (NT 4.0, 2000, XP, 2003, Vista+):透過未文件化的原生 API NtQuerySystemInformation 讀取系統效能計數器 (閒置時間、系統時間)。
  • Windows 9x 系列 (95, 98, Me):透過動態登錄檔 HKEY_DYN_DATA 讀取虛擬裝置驅動程式 (VxD) 維護的統計計數器。

本文提供兩套完整 C 語言主控台程式碼,分別示範兩種平台的實作邏輯。

方案一:Windows NT/2000/XP/2003 — NtQuerySystemInformation

核心原理

CPU 使用率 = 100% - (閒置時間增量 / 系統時間增量) * 100% / 處理器數

  • 系統時間:所有處理器總執行時間 (含核心模式 + 使用者模式)。
  • 閒置時間:系統執行「系統閒置程序」的時間總和。
  • 取樣間隔通常 1 秒,計算兩次取樣的差值。

關鍵結構與常數

// 系統資訊類別代碼 (未文件化)
#define SystemBasicInformation         0  // 取得處理器數量等基本資訊
#define SystemPerformanceInformation   2  // 取得閒置時間 (LARGE_INTEGER)
#define SystemTimeInformation          3  // 取得系統啟動/當前時間

// 從 ntdll.dll 動態取得函式指標
typedef LONG (WINAPI *PFN_NtQuerySystemInformation)(
  UINT SystemInformationClass,
  PVOID SystemInformation,
  ULONG SystemInformationLength,
  PULONG ReturnLength
);

完整運作流程

  1. LoadLibrary("ntdll.dll") + GetProcAddress("NtQuerySystemInformation") 取得函式指標。
  2. 呼叫 SystemBasicInformation 取得 bKeNumberProcessors (邏輯處理器數)。
  3. 迴圈 (每秒):
    a. 呼叫 SystemTimeInformation 取得 liKeSystemTime (系統時間)。
    b. 呼叫 SystemPerformanceInformation 取得 liIdleTime (閒置時間)。
    c. 若非第一次取樣,計算差值並換算使用率。
    d. 更新舊值,Sleep(1000)。

完整可編譯原始碼 (Visual C++ / GCC 相容)

#include 
#include 
#include 

// LARGE_INTEGER 轉 double 輔助宏
#define Li2Double(li) ((double)(li).HighPart * 4.294967296E9 + (double)(li).LowPart)

typedef struct { // SystemBasicInformation 部分欄位
  BYTE bKeNumberProcessors;
  // ... 其餘略
} SYSTEM_BASIC_INFORMATION;

typedef struct { // SystemPerformanceInformation
  LARGE_INTEGER liIdleTime;
  DWORD dwSpare[76];
} SYSTEM_PERFORMANCE_INFORMATION;

typedef struct { // SystemTimeInformation
  LARGE_INTEGER liKeSystemTime;
  // ... 其餘略
} SYSTEM_TIME_INFORMATION;

int main() {
  PFN_NtQuerySystemInformation NtQuerySystemInformation = 
    (PFN_NtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
  if (!NtQuerySystemInformation) { puts("無法取得 NtQuerySystemInformation"); return 1; }

  SYSTEM_BASIC_INFORMATION BasicInfo;
  if (NtQuerySystemInformation(SystemBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL) != 0)
    return 1;

  SYSTEM_PERFORMANCE_INFORMATION PerfInfo;
  SYSTEM_TIME_INFORMATION TimeInfo;
  LARGE_INTEGER OldIdle = {0}, OldSys = {0};
  double cpuUsage = 0.0;

  printf("CPU Usage (press any key to exit): ");
  while (!_kbhit()) {
    if (NtQuerySystemInformation(SystemTimeInformation, &TimeInfo, sizeof(TimeInfo), NULL) != 0) break;
    if (NtQuerySystemInformation(SystemPerformanceInformation, &PerfInfo, sizeof(PerfInfo), NULL) != 0) break;

    if (OldIdle.QuadPart != 0) { // 非第一次取樣
      double idleDelta = Li2Double(PerfInfo.liIdleTime) - Li2Double(OldIdle);
      double sysDelta  = Li2Double(TimeInfo.liKeSystemTime) - Li2Double(OldSys);
      double idleRatio = idleDelta / sysDelta; // 0.0 ~ 1.0
      cpuUsage = 100.0 - idleRatio * 100.0 / BasicInfo.bKeNumberProcessors;
      if (cpuUsage < 0) cpuUsage = 0;
      if (cpuUsage > 100) cpuUsage = 100;
      printf("\b\b\b\b%3.0f%%", cpuUsage);
    }

    OldIdle = PerfInfo.liIdleTime;
    OldSys  = TimeInfo.liKeSystemTime;
    Sleep(1000);
  }
  printf("\n");
  return 0;
}

方案二:Windows 95/98/Me — HKEY_DYN_DATA

核心原理

Win9x 核心虛擬裝置驅動程式 (VxD) 定期更新動態登錄檔樹 HKEY_DYN_DATA 下的效能計數器:

  • PerfStats\StartStat:啟動計數器 (需先讀取一次啟動)。
  • PerfStats\StatData:目前計數器值,KERNEL\CPUUsage 即為 0~100 整數百分比。
  • PerfStats\StopStat:停止計數器 (程式結束前讀取一次)。

完整原始碼

#include 
#include 
#include 

int main() {
  HKEY hKey;
  DWORD dwType, dwSize, dwCpuUsage;

  // 1. 啟動計數器
  if (RegOpenKeyEx(HKEY_DYN_DATA, "PerfStats\\StartStat", 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
    return 1;
  dwSize = sizeof(DWORD);
  RegQueryValueEx(hKey, "KERNEL\\CPUUsage", NULL, &dwType, (LPBYTE)&dwCpuUsage, &dwSize);
  RegCloseKey(hKey);

  // 2. 迴圈讀取目前值
  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()) {
    dwSize = sizeof(DWORD);
    if (RegQueryValueEx(hKey, "KERNEL\\CPUUsage", NULL, &dwType, (LPBYTE)&dwCpuUsage, &dwSize) == ERROR_SUCCESS)
      printf("\b\b\b\b%3d%%", dwCpuUsage);
    Sleep(500); // 9x 更新頻率較高,可 500ms 取樣
  }
  printf("\n");
  RegCloseKey(hKey);

  // 3. 停止計數器 (清理)
  if (RegOpenKeyEx(HKEY_DYN_DATA, "PerfStats\\StopStat", 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) {
    dwSize = sizeof(DWORD);
    RegQueryValueEx(hKey, "KERNEL\\CPUUsage", NULL, &dwType, (LPBYTE)&dwCpuUsage, &dwSize);
    RegCloseKey(hKey);
  }
  return 0;
}

現代替代方案 (Vista / Windows 7 / 10 / 11)

雖然 NtQuerySystemInformation 至今仍可用,但微軟建議改用正式文件化的 API:

  • PDH (Performance Data Helper):PdhOpenQuery, PdhAddCounter (路徑 "\\Processor(_Total)\\% Processor Time"), PdhCollectQueryData, PdhGetFormattedCounterValue。支援本機/遠端、多語系、格式化輸出。
  • WMI (Win32_Processor):SELECT LoadPercentage FROM Win32_Processor,適合腳本/管理工具,即時性較低。
  • GetSystemTimes (Kernel32, XP+):取得 lpIdleTime, lpKernelTime, lpUserTime,邏輯與 NtQuerySystemInformation 相同但為正式 API。

跨平台封裝建議

double GetCpuUsage() {
  OSVERSIONINFOEX osvi = { sizeof(osvi) };
  GetVersionEx((OSVERSIONINFO*)&osvi);
  if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
    return GetCpuUsage_NT();   // 使用 GetSystemTimes 或 PDH
  else
    return GetCpuUsage_9x();   // 僅維舊系統相容
}

總結比較表

特性NT 方案 (NtQuerySystemInformation)9x 方案 (HKEY_DYN_DATA)現代建議 (PDH / GetSystemTimes)
適用平台NT 4.0 ~ Windows 1195 / 98 / MeXP / 2003 ~ Windows 11
API 性質未文件化、內部函式虛擬登錄檔、VxD 介面正式文件化、穩定支援
精度高 (100ns 解析度)整數百分比、更新較頻繁高
多處理器支援自動平均單一數值可分別查詢每顆處理器
部署相依性無 (ntdll.dll 核心載入)無PDH 需連結 pdh.lib / 載入 pdh.dll
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved