You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows平台下C程序监控其他进程CPU使用率的实现方法

Windows下用C程序获取任意进程CPU使用率的实现方法

要在Windows下获取任意进程的CPU使用率,核心思路是通过两次时间采样计算差值(单次获取的是累计时间,无法直接得到实时使用率),结合系统级和进程级的时间统计API来实现。以下是具体步骤和代码示例:

核心依赖的Windows API

需要用到这些头文件和库:

  • 头文件:windows.h、psapi.h
  • 链接库:psapi.lib、kernel32.lib

实现步骤

  1. 枚举系统中所有进程ID
    使用EnumProcesses函数获取当前运行的所有进程ID,需注意缓冲区大小——若第一次调用返回的进程数等于缓冲区容量,需扩容后重新调用。

  2. 打开进程并获取权限
    对每个进程ID,调用OpenProcess获取进程句柄,需申请PROCESS_QUERY_INFORMATION和PROCESS_VM_READ权限。部分系统进程可能无法打开,需处理这类错误。

  3. 两次采样时间数据

    • 第一次采样:调用GetProcessTimes获取进程的用户态/内核态累计时间,同时调用GetSystemTimes获取系统的空闲时间、内核态/用户态总时间。
    • 等待一段固定时间(比如1秒),进行第二次采样,获取相同的时间数据。
  4. 计算CPU使用率
    通过两次采样的时间差值,计算进程占用的CPU比例:

    • 系统总时间差 = (第二次系统用户时间 + 第二次系统内核时间) - (第一次系统用户时间 + 第一次系统内核时间)
    • 进程时间差 = (第二次进程用户时间 + 第二次进程内核时间) - (第一次进程用户时间 + 第一次进程内核时间)
    • 进程CPU使用率 = (进程时间差.QuadPart * 100.0) / 系统总时间差.QuadPart

    注:该结果是进程相对于系统总CPU资源的使用率(比如4核系统中,单核心满负载的进程使用率为25%)。

  5. 阈值检测
    将计算出的使用率与设定阈值对比,标记超过阈值的进程。

示例代码

#include <windows.h>
#include <psapi.h>
#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>

// 设定CPU使用率阈值(比如50%)
#define CPU_THRESHOLD 50.0

// 存储时间采样数据的结构体
typedef struct {
    FILETIME process_user;
    FILETIME process_kernel;
    FILETIME system_idle;
    FILETIME system_kernel;
    FILETIME system_user;
} TimeSample;

// 采样一次时间数据
BOOL TakeSample(DWORD process_id, TimeSample* sample) {
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id);
    if (hProcess == NULL) {
        return FALSE;
    }

    // 获取进程时间
    if (!GetProcessTimes(hProcess, NULL, NULL, &sample->process_kernel, &sample->process_user)) {
        CloseHandle(hProcess);
        return FALSE;
    }

    // 获取系统时间
    if (!GetSystemTimes(&sample->system_idle, &sample->system_kernel, &sample->system_user)) {
        CloseHandle(hProcess);
        return FALSE;
    }

    CloseHandle(hProcess);
    return TRUE;
}

// 计算CPU使用率
double CalculateCpuUsage(const TimeSample* first, const TimeSample* second) {
    // 计算系统总时间差
    ULONGLONG system_total_first = (ULONGLONG)first->system_user.dwHighDateTime << 32 | first->system_user.dwLowDateTime;
    system_total_first += (ULONGLONG)first->system_kernel.dwHighDateTime << 32 | first->system_kernel.dwLowDateTime;
    
    ULONGLONG system_total_second = (ULONGLONG)second->system_user.dwHighDateTime << 32 | second->system_user.dwLowDateTime;
    system_total_second += (ULONGLONG)second->system_kernel.dwHighDateTime << 32 | second->system_kernel.dwLowDateTime;
    
    ULONGLONG system_diff = system_total_second - system_total_first;
    if (system_diff == 0) {
        return 0.0;
    }

    // 计算进程时间差
    ULONGLONG process_total_first = (ULONGLONG)first->process_user.dwHighDateTime << 32 | first->process_user.dwLowDateTime;
    process_total_first += (ULONGLONG)first->process_kernel.dwHighDateTime << 32 | first->process_kernel.dwLowDateTime;
    
    ULONGLONG process_total_second = (ULONGLONG)second->process_user.dwHighDateTime << 32 | second->process_user.dwLowDateTime;
    process_total_second += (ULONGLONG)second->process_kernel.dwHighDateTime << 32 | second->process_kernel.dwLowDateTime;
    
    ULONGLONG process_diff = process_total_second - process_total_first;

    return (double)process_diff * 100.0 / system_diff;
}

int _tmain() {
    DWORD process_ids[1024];
    DWORD bytes_returned;

    // 枚举所有进程ID
    if (!EnumProcesses(process_ids, sizeof(process_ids), &bytes_returned)) {
        _tprintf(_T("EnumProcesses failed. Error: %lu\n"), GetLastError());
        return 1;
    }

    DWORD process_count = bytes_returned / sizeof(DWORD);

    // 第一次采样所有进程的时间数据
    TimeSample* first_samples = malloc(process_count * sizeof(TimeSample));
    if (first_samples == NULL) {
        _tprintf(_T("Memory allocation failed.\n"));
        return 1;
    }

    for (DWORD i = 0; i < process_count; i++) {
        TakeSample(process_ids[i], &first_samples[i]);
    }

    // 等待1秒,让时间产生差值
    Sleep(1000);

    // 第二次采样并计算使用率
    for (DWORD i = 0; i < process_count; i++) {
        TimeSample second_sample;
        if (!TakeSample(process_ids[i], &second_sample)) {
            continue; // 进程已退出或无法访问
        }

        double cpu_usage = CalculateCpuUsage(&first_samples[i], &second_sample);
        if (cpu_usage >= CPU_THRESHOLD) {
            // 获取进程名称
            TCHAR process_name[MAX_PATH] = _T("<unknown>");
            HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_ids[i]);
            if (hProcess != NULL) {
                HMODULE hModule;
                DWORD bytes_needed;
                if (EnumProcessModules(hProcess, &hModule, sizeof(hModule), &bytes_needed)) {
                    GetModuleBaseName(hProcess, hModule, process_name, sizeof(process_name)/sizeof(TCHAR));
                }
                CloseHandle(hProcess);
            }

            _tprintf(_T("Process %s (PID: %lu) CPU usage: %.1f%% (exceeds threshold of %.1f%%)\n"), 
                     process_name, process_ids[i], cpu_usage, CPU_THRESHOLD);
        }
    }

    free(first_samples);
    return 0;
}

注意事项

  • 权限问题:部分系统进程需要管理员权限才能打开,建议以管理员身份运行程序,避免无法获取部分进程的信息。
  • 采样间隔:示例中使用1秒间隔,间隔过短会导致时间差值过小,计算结果误差大;间隔过长则实时性降低,可根据需求调整。
  • 进程退出处理:两次采样之间可能有进程退出,第二次OpenProcess或GetProcessTimes会失败,需跳过这类进程。
  • 编译设置:编译时需链接psapi.lib(比如在VS中添加#pragma comment(lib, "psapi.lib"))。

内容的提问来源于stack exchange,提问作者KemVolt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 23:44:56