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

macOS编程获取进程CPU使用率:计算值异常问题排查

macOS进程CPU使用率计算问题

我想在macOS上用Windows上可行的方法计算自身进程的当前CPU使用率,公式如下:

(currentAppTime - lastTrackedAppTime) * 100% / (currentSysTime - lastTrackedSysTime)

其中lastTracked和current指标是间隔一段时间收集的。

我分别获取总CPU时间和进程时间的代码如下:

总CPU时间代码

mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpuinfo, &count);
lastTrackedSysTime = std::accumulate(cpuinfo.cpu_ticks, cpuinfo.cpu_ticks + count, 0ULL);

进程时间代码

struct proc_taskinfo taskInfo;
proc_pidinfo(getpid(), PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo));
lastTrackedAppTime = taskInfo.pti_total_user + taskInfo.pti_total_system;

我的完整实现

static uint64_t lastTrackedAppTime; // 已在构造函数中初始化
static uint64_t lastTrackedSysTime; // 已在构造函数中初始化

void AppMetrics::getCPU() // 每5秒调用一次
{
    host_cpu_load_info_data_t cpuinfo;
    mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
    auto hs = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpuinfo, &count);
    assert(hs == KERN_SUCCESS);

    uint64_t currentSysTime = std::accumulate(cpuinfo.cpu_ticks, cpuinfo.cpu_ticks + count, 0ULL);
    uint64_t totalSysDiff = currentSysTime - lastTrackedSysTime;

    struct proc_taskinfo taskInfo;
    auto ppi = proc_pidinfo(getpid(), PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo));
    assert(ppi == sizeof(taskInfo));
    
    uint64_t currentAppTime = taskInfo.pti_total_user + taskInfo.pti_total_system;
    uint64_t totalAppDiff = currentAppTime - lastTrackedAppTime;
    metrics.appCPU = totalAppDiff * 100.0 / totalSysDiff; // 我需要的数值

    lastTrackedSysTime = currentSysTime;
    lastTrackedAppTime = currentAppTime;
}

问题在于totalAppDiff远大于totalSysDiff,具体数值示例:

lastTrackedSysTime: 4555257262
currentSysTime: 4555261249
totalSysDiff: 3987

lastTrackedAppTime: 435637867
currentAppTime: 441909402
totalAppDiff: 6271535

请问这种情况是如何发生的?我哪里出错了?这个计算方法本身是否正确?


问题根源与解决方案

核心错误:时间单位不匹配

你遇到的问题本质是两个时间值的计量单位完全不同:

  • host_statistics返回的cpu_ticks是系统时钟滴答数,单位由系统时钟中断频率决定(比如早期macOS是100滴答/秒,现代系统可能更高);
  • proc_pidinfo返回的pti_total_user和pti_total_system是微秒(1微秒=10^-6秒)。

这直接导致totalAppDiff(微秒级)和totalSysDiff(滴答级)的数值不在同一量级,自然出现进程时间差远大于系统时间差的结果。

方法本身的合理性

Windows上的这个计算逻辑是正确的,只要在macOS上保证两个时间的计量单位统一,就可以复用该公式。

修正方案

方案1:统一为微秒单位

先将系统CPU时间的滴答数转换为微秒,再与进程时间计算比值:

  1. 先获取系统时钟滴答频率(每秒的滴答数):
#include <sys/sysctl.h>

uint64_t getTickRate() {
    uint64_t tickRate;
    size_t len = sizeof(tickRate);
    sysctlbyname("hw.tick", &tickRate, &len, NULL, 0);
    return tickRate;
}
  1. 修改getCPU函数,统一单位后计算:
// 提前初始化一次,无需每次调用都获取
static uint64_t tickRate = getTickRate();

void AppMetrics::getCPU() // 每5秒调用一次
{
    host_cpu_load_info_data_t cpuinfo;
    mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
    auto hs = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpuinfo, &count);
    assert(hs == KERN_SUCCESS);

    uint64_t currentSysTicks = std::accumulate(cpuinfo.cpu_ticks, cpuinfo.cpu_ticks + count, 0ULL);
    uint64_t sysTicksDiff = currentSysTicks - lastTrackedSysTime;
    // 将滴答数转换为微秒
    double totalSysDiffUs = sysTicksDiff * (1000000.0 / tickRate);

    struct proc_taskinfo taskInfo;
    auto ppi = proc_pidinfo(getpid(), PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo));
    assert(ppi == sizeof(taskInfo));
    
    uint64_t currentAppTime = taskInfo.pti_total_user + taskInfo.pti_total_system;
    uint64_t totalAppDiffUs = currentAppTime - lastTrackedAppTime;
    // 单位统一后计算CPU使用率
    metrics.appCPU = (totalAppDiffUs / totalSysDiffUs) * 100.0;

    lastTrackedSysTime = currentSysTicks;
    lastTrackedAppTime = currentAppTime;
}

方案2:改用Mach API获取进程时间(与系统时间单位一致)

使用task_info获取进程时间,其单位与host_statistics的滴答数一致:

#include <mach/mach.h>

void AppMetrics::getCPU() // 每5秒调用一次
{
    host_cpu_load_info_data_t cpuinfo;
    mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
    auto hs = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpuinfo, &count);
    assert(hs == KERN_SUCCESS);

    uint64_t currentSysTicks = std::accumulate(cpuinfo.cpu_ticks, cpuinfo.cpu_ticks + count, 0ULL);
    uint64_t totalSysDiff = currentSysTicks - lastTrackedSysTime;

    // 获取进程线程时间信息
    task_thread_times_info_data_t threadTimes;
    mach_msg_type_number_t threadCount = TASK_THREAD_TIMES_INFO_COUNT;
    kern_return_t kr = task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t)&threadTimes, &threadCount);
    assert(kr == KERN_SUCCESS);
    
    // 将Mach时间转换为系统滴答数
    mach_timebase_info_data_t timeBase;
    mach_timebase_info(&timeBase);
    // 转换为纳秒
    uint64_t userNano = threadTimes.user_time * timeBase.numer / timeBase.denom;
    uint64_t systemNano = threadTimes.system_time * timeBase.numer / timeBase.denom;
    // 转换为系统滴答数
    uint64_t currentAppTime = (userNano + systemNano) / (1000000000.0 / tickRate);

    uint64_t totalAppDiff = currentAppTime - lastTrackedAppTime;
    metrics.appCPU = (double)totalAppDiff * 100.0 / totalSysDiff;

    lastTrackedSysTime = currentSysTicks;
    lastTrackedAppTime = currentAppTime;
}

额外注意事项

std::accumulate(cpuinfo.cpu_ticks, cpuinfo.cpu_ticks + count, 0ULL)计算的是所有CPU核心的总运行时间(包括空闲时间),因此最终得到的是进程占用全部CPU资源的比例。如果需要计算进程占用单个核心的使用率,需要将结果除以系统核心数(可通过sysctlbyname("hw.ncpu", ...)获取)。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 07:39:55