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

Windows 即時獲取進程 CPU 佔用率:基於系統時間差分算法的 C++ 實現

     編輯:VC 教學

核心原理:時間差分法

Windows 不直接提供「進程 CPU %」API。標準做法:

  1. 採樣時刻 T1:讀取目標進程 User Time + Kernel Time (100ns 為單位),同時讀取 **系統總 CPU 時間** (所有邏輯處理器之和)。
  2. 採樣時刻 T2 (間隔 Δt,建議 500ms~1s):再次讀取上述兩值。
  3. 計算:
    Process_CPU_% = ( (Proc_T2 - Proc_T1) / (Sys_T2 - Sys_T1) ) * 100%
    其中分母為系統總時間增量 (已包含多核心並行),結果即為該進程佔總 CPU 資源的百分比。

關鍵結構體定義 (來自 ntdll.dll 未公開 API)

typedef struct _THREAD_INFO {
    LARGE_INTEGER CreateTime;
    DWORD dwUnknown1;
    DWORD dwStartAddress;
    DWORD StartEIP;
    DWORD dwOwnerPID;
    DWORD dwThreadId;
    DWORD dwCurrentPriority;
    DWORD dwBasePriority;
    DWORD dwContextSwitches;
    DWORD Unknown;
    DWORD WaitReason;
} THREADINFO, *PTHREADINFO;

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaxLength;
    PWSTR  Buffer;
} UNICODE_STRING;

typedef struct _PROCESS_INFO {
    DWORD dwOffset;            // 到下一個 PROCESS_INFO 的偏移
    DWORD dwThreadsCount;
    DWORD dwUnused1[6];
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER UserTime;    // 用戶態 CPU 時間 (100ns)
    LARGE_INTEGER KernelTime;  // 核心態 CPU 時間 (100ns)
    UNICODE_STRING ProcessName;
    DWORD dwBasePriority;
    DWORD dwProcessID;
    DWORD dwParentProcessId;
    DWORD dwHandleCount;
    DWORD dwUnused3[2];
    DWORD dwVirtualBytesPeak;
    DWORD dwVirtualBytes;
    ULONG dwPageFaults;
    DWORD dwWorkingSetPeak;
    DWORD dwWorkingSet;
    DWORD dwQuotaPeakPagedPoolUsage;
    DWORD dwQuotaPagedPoolUsage;
    DWORD dwQuotaPeakNonPagedPoolUsage;
    DWORD dwQuotaNonPagedPoolUsage;
    DWORD dwPageFileUsage;
    DWORD dwPageFileUsagePeak;
    DWORD dCommitCharge;
    THREADINFO ThreadSysInfo[1]; // 變長數組
} PROCESSINFO, *PPROCESSINFO;

獲取數據的兩種途徑

方法API優點缺點
文檔化 APIOpenProcess + GetProcessTimes穩定、有簽名、支援遠程進程需 PROCESS_QUERY_LIMITED_INFORMATION 權限;頻繁調用開銷大
系統信息類NtQuerySystemInformation(SystemProcessInformation, ...)一次調用獲取**所有進程/線程**完整信息,極快非公開 API (但極穩定)、需動態載入 ntdll、結構體對齊敏感

高性能實現範例 (NtQuerySystemInformation)

#include 
#include  // 需 Windows SDK 10+ 或自行定義上述結構
#pragma comment(lib, "ntdll.lib")

typedef NTSTATUS (NTAPI *PNtQuerySystemInformation)(
    SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);

double GetProcessCpuPercent(DWORD pid) {
    static PNtQuerySystemInformation pNtQSI = nullptr;
    if (!pNtQSI) pNtQSI = (PNtQuerySystemInformation)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQuerySystemInformation");

    // 兩次採樣
    auto snap = [&](ULONGLONG& procTime, ULONGLONG& sysTime) {
        ULONG len = 0; pNtQSI(SystemProcessInformation, nullptr, 0, &len);
        std::vector buf(len);
        pNtQSI(SystemProcessInformation, buf.data(), len, &len);

        ULONGLONG totalSys = 0;
        PPROCESSINFO p = (PPROCESSINFO)buf.data();
        while (true) {
            ULONGLONG ut = p->UserTime.QuadPart;
            ULONGLONG kt = p->KernelTime.QuadPart;
            totalSys += ut + kt; // 累加所有進程時間 = 系統總 CPU 時間
            if (p->dwProcessID == pid) {
                procTime = ut + kt;
            }
            if (p->dwOffset == 0) break;
            p = (PPROCESSINFO)((BYTE*)p + p->dwOffset);
        }
        sysTime = totalSys;
    };

    ULONGLONG proc1, sys1, proc2, sys2;
    snap(proc1, sys1);
    Sleep(500); // 採樣間隔
    snap(proc2, sys2);

    ULONGLONG dProc = proc2 - proc1;
    ULONGLONG dSys  = sys2  - sys1;
    if (dSys == 0) return 0.0;
    return (double)dProc / dSys * 100.0; // 已包含多核心 (系統總時間 = 核心數 * 實時)
}

進階優化與避坑

  • 多核心正確性:分母使用「所有邏輯處理器總時間」,結果直接為「佔總 CPU 百分比」(單核 100%,雙核 200%...);若需「單核百分比」,除以邏輯處理器數 (GetSystemInfo / GetLogicalProcessorInformation)。
  • 首次採樣噪聲:首次調用 NtQuerySystemInformation 可能較慢,建議啟動時預熱一次。
  • 權限:查詢系統進程 (PID 0, 4 等) 需 SeDebugPrivilege / 管理員權限。
  • 替代方案:PDH (Performance Data Helper) PdhAddCounter("\\Process(*)\\% Processor Time") 封裝好但延遲較大;WMI Win32_PerfFormattedData_PerfProc_Process 適合腳本/低頻查詢。

總結

掌握 NtQuerySystemInformation + 時間差分算法,即可在 C++ 中以極低開銷實現即時、準確的進程 CPU 監控,適用於任務管理器、性能分析器、資源限制守護進程等場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved