M1芯片macOS下proc_pid_rusage计算进程CPU使用率结果异常的技术咨询
计算逻辑存在的核心缺陷
- 时间单位换算的硬编码假设不成立。
proc_pid_rusage接口返回的rusage_info_v4结构体中,ri_user_time、ri_system_time存储的是系统绝对时间计数(tick),并非直接可读的纳秒值。不同硬件架构下,tick到纳秒的换算比例由系统硬件时钟决定:部分Intel芯片该换算比例恰好为1:1,因此原代码硬编码除以1e9(默认1tick=1纳秒)的写法凑巧能得到接近正确的结果;但在M系列Apple Silicon芯片上,单tick通常对应数十纳秒,原代码直接将tick数当作纳秒值参与计算,会把实际消耗的CPU时间缩小数十倍,最终计算出的使用率自然远低于真实值。 - 采样间隔的硬编码存在误差隐患。原代码直接将10秒作为固定计算分母,没有考虑系统调度导致的休眠偏移,一旦实际采样时长和预设的10秒存在偏差,计算结果也会同步出现误差。
M1芯片上计算偏差的修复方案
按照以下逻辑调整代码即可对齐活动监视器的观测结果:
- 替换硬编码的时间换算逻辑。通过
mach_timebase_info接口动态获取当前系统的tick到纳秒的换算比例,不再默认1tick等于1纳秒。 - 实测两次采样的真实间隔作为计算分母。在两次调用
proc_pid_rusage的同时,用mach_absolute_time()记录对应的墙上时间,计算两次采样的真实时长,替代硬编码的10秒间隔。 - 对齐活动监视器的统计口径。活动监视器的CPU百分比计算逻辑为:以单核心被完全占满作为100%的基准,进程占满N个核心时显示的数值为N*100,该口径和
proc_pid_rusage返回的累计CPU时间统计完全匹配。
核心计算公式:CPU使用率 = (两次采样间进程累计消耗的CPU时间,换算为秒) / (两次采样的真实间隔,秒) * 100
修正后的核心计算片段参考:
// 第一次采样 struct rusage_info_v4 ru; int ret = proc_pid_rusage(pid, RUSAGE_INFO_V4, (rusage_info_t)&ru); if (ret != 0) { /* 错误处理 */ } uint64_t wall_start = mach_absolute_time(); // 固定间隔休眠 std::this_thread::sleep_for(std::chrono::seconds(10)); // 第二次采样 struct rusage_info_v4 ru2; ret = proc_pid_rusage(pid, RUSAGE_INFO_V4, (rusage_info_t)&ru2); if (ret != 0) { /* 错误处理 */ } uint64_t wall_end = mach_absolute_time(); // 动态获取系统时钟换算参数 mach_timebase_info_data_t timebase; mach_timebase_info(&timebase); double ns_per_tick = static_cast<double>(timebase.numer) / timebase.denom; // 计算两次采样间的CPU时间差(纳秒) uint64_t cpu_tick_diff = (ru2.ri_user_time + ru2.ri_system_time) - (ru.ri_user_time + ru.ri_system_time); double cpu_time_ns = cpu_tick_diff * ns_per_tick; // 计算两次采样的真实间隔(纳秒) double interval_ns = (wall_end - wall_start) * ns_per_tick; // 计算CPU使用率 double cpu_usage = (cpu_time_ns / interval_ns) * 100;
内容的提问来源于stack exchange,提问作者聂小涛
相关产品推荐
相关产品推荐

