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时间的滴答数转换为微秒,再与进程时间计算比值:
- 先获取系统时钟滴答频率(每秒的滴答数):
#include <sys/sysctl.h> uint64_t getTickRate() { uint64_t tickRate; size_t len = sizeof(tickRate); sysctlbyname("hw.tick", &tickRate, &len, NULL, 0); return tickRate; }
- 修改
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
相关产品推荐
相关产品推荐

