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

Windows 系統 CPU 使用率獲取完整指南:NT/2000 與 9x 兩種實現

     編輯:VC 教學

前言

即時監控 CPU 使用率是系統監控工具、任務管理器類應用的核心功能。Windows NT 內核系列(NT/2000/XP/Server)與 Windows 9x 系列(95/98/Me)採用完全不同的機制獲取 CPU 使用率。本文詳細解析兩種平台的實現原理,提供可直接編譯運行的 C/C++ 完整代碼。

第一部分:Windows NT/2000/XP/Server 系列 - 系統效能計數器法

核心原理

NT 內核通過 NtQuerySystemInformation 系統呼叫提供三類關鍵資訊:

  • SystemBasicInformation (0):系統基礎資訊,包含處理器數量 bKeNumberProcessors
  • SystemPerformanceInformation (2):效能資訊,包含系統閒置時間 liIdleTime(所有處理器閒置時間總和,100 奈秒單位)
  • SystemTimeInformation (3):系統時間資訊,包含系統運行總時間 liKeSystemTime

CPU 使用率計算公式:

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;
}

第二部分:Windows 95/98/Me - 動態登錄效能計數器法

核心原理

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();
    }
};

注意事項與現代替代方案

  • NT 系列:NtQuerySystemInformation 為非文檔化 API,雖極其穩定但微軟不保證長期相容。現代開發建議使用 PDH (Performance Data Helper) 或 WMI (Win32_Processor.LoadPercentage)
  • 9x 系列:HKEY_DYN_DATA 僅存在於 9x,NT 系統無此鍵
  • 多核心:NT 代碼已正確處理多處理器(除以 bKeNumberProcessors)
  • 權限:NT 代碼需標準用戶權限即可;PDH/WMI 可能需管理員權限
  • 精度:採樣間隔建議 500ms-1s,過短會增加系統負載
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved