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

Windows 即時取得指定進程 CPU 使用率:NtQuerySystemInformation 非文檔 API 實作

     編輯:關於C++

工作管理員的「程序」頁籤能顯示每個進程的 CPU 佔用百分比。本文揭示其核心算法:取樣間隔內,目標進程核心+使用者模式時間增量 / 系統總 CPU 時間增量,並提供以 NtQuerySystemInformation(SystemProcessInformation) 非文檔 API 實作的 C/C++ 函數,傳入 PID 即時回傳使用率。

一、核心資料結構(逆向工程定義)

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

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

typedef struct _PROCESS_INFO {
    DWORD dwOffset;            // 下一個 PROCESS_INFO 相對位移
    DWORD dwThreadCount;
    DWORD dwUnused1[6];
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER UserTime;    // 使用者模式 CPU 時間 (100ns 單位)
    LARGE_INTEGER KernelTime;  // 核心模式 CPU 時間
    UNICODE_STRING ProcessName;
    DWORD dwBasePriority, dwProcessID, dwParentProcessId;
    DWORD dwHandleCount, dwUnused3[2];
    DWORD dwVirtualBytesPeak, dwVirtualBytes;
    ULONG dwPageFaults;
    DWORD dwWorkingSetPeak, dwWorkingSet;
    DWORD dwQuotaPeakPagedPoolUsage, dwQuotaPagedPoolUsage;
    DWORD dwQuotaPeakNonPagedPoolUsage, dwQuotaNonPagedPoolUsage;
    DWORD dwPageFileUsage, dwPageFileUsagePeak;
    DWORD dCommitCharge;
    THREADINFO ThreadSysInfo[1]; // 變長陣列
} PROCESSINFO, *PPROCESSINFO;

二、演算法邏輯

  1. 呼叫 NtQuerySystemInformation(5, buffer, size, 0) 取得所有進程鏈表
  2. 遍歷鏈表累加 所有進程 的 UserTime + KernelTime → TotalProcessCPUUsage
  3. 同步累加 目標 PID 的 UserTime + KernelTime → CurrentProcessCPUUsage
  4. 計算增量:
    TotalDelta    = TotalProcessCPUUsage    - LastTotalProcessCPUUsage;
    CurrentDelta = CurrentProcessCPUUsage - LastCurrentProcessCPUUsage;
  5. 使用率 = 100 * CurrentDelta / TotalDelta(TotalDelta > 0)
  6. 更新 Last* 變數供下次取樣

三、完整函數實作

int GetProcessCpuUsage(DWORD dwPID) {
    static __int64 LastTotal = 0, LastCurrent = 0;
    int cpuUsage = 0;

    // 動態取得 NtQuerySystemInformation
    typedef LONG (NTAPI *PFN_NtQSI)(ULONG, PVOID, ULONG, PULONG);
    static PFN_NtQSI pNtQSI = nullptr;
    if (!pNtQSI) {
        HMODULE hNtdll = GetModuleHandle(_T("ntdll.dll"));
        pNtQSI = (PFN_NtQSI)GetProcAddress(hNtdll, "NtQuerySystemInformation");
        if (!pNtQSI) return -1;
    }

    const DWORD BUFSIZE = 0x20000; // 128KB,視進程數調整
    BYTE* pBuf = new BYTE[BUFSIZE];
    ULONG retLen = 0;
    LONG status = pNtQSI(5, pBuf, BUFSIZE, &retLen); // SystemProcessInformation = 5
    if (status != 0) { delete[] pBuf; return -1; }

    PPROCESSINFO pInfo = (PPROCESSINFO)pBuf;
    __int64 total = 0, current = 0;

    while (true) {
        __int64 procTime = pInfo->UserTime.QuadPart + pInfo->KernelTime.QuadPart;
        total += procTime;
        if (pInfo->dwProcessID == dwPID) current += procTime;

        if (pInfo->dwOffset == 0) break;
        pInfo = (PPROCESSINFO)((BYTE*)pInfo + pInfo->dwOffset);
    }

    __int64 totalDelta = total - LastTotal;
    __int64 curDelta   = current - LastCurrent;
    if (totalDelta > 0) cpuUsage = (int)(100 * curDelta / totalDelta);

    LastTotal = total;
    LastCurrent = current;
    delete[] pBuf;
    return cpuUsage; // 0~100
}

四、主控台測試程式

int main() {
    DWORD pid = 1234; // 目標 PID,0 表示系統閒置進程
    while (true) {
        int cpu = GetProcessCpuUsage(pid);
        printf("PID %u CPU: %d%%\n", pid, cpu);
        Sleep(1000); // 1 秒取樣間隔
    }
    return 0;
}

五、關鍵注意事項

議題說明
非文檔 APINtQuerySystemInformation 微軟保留變更權利,但 Windows 2000~11 穩定相容
權限需 PROCESS_QUERY_LIMITED_INFORMATION,通常標準使用者即可查詢自身/同會話進程
緩衝區大小進程數超過 128KB 會返回 STATUS_INFO_LENGTH_MISMATCH,需迴圈加倍重試
多核心正規化此算法回傳「相對單核心 100%」,如 4 核心滿載單進程可達 400%,如需「相對總核心 100%」再除以邏輯處理器數
PID=0 特殊含義系統閒置進程,CPU 使用率 = 100 - 總使用率
替代方案PDH (PdhAddCounter \Process(%)\% Processor Time)、WMI、GetProcessTimes(需自行排程取樣)

六、總結

以 NtQuerySystemInformation 遍歷系統進程資訊塊,雙取樣差分計算 CPU 使用率,是任務管理器、效能監視器、APM 探針的共同基石。雖屬非文檔介面,但在 Windows 生態極為穩定;現代專案建議優先使用 PDH/ETW 文檔化 API 以降低維護風險。

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