Linux下计算每核CPU占用率需两次采样/proc/stat中各cpu行前5字段之和与idle值,代入公式(total_time−idle_time)/total_time×100;实时频率须读/sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq(单位kHz),注意权限、offline状态及ARM平台fallback。

Linux下用/proc/stat和/proc/[pid]/stat算每核CPU占用率
直接读/proc/stat能拿到系统级各CPU核心的user、nice、system、idle、iowait等计数(单位是jiffies),但注意:这些是自启动以来的累计值,不是瞬时百分比。要算“当前占用率”,必须做两次采样、相减再归一化。
关键点:
- 每次读/proc/stat后,跳过第一行(cpu总和),按cpu0、cpu1…顺序解析后续行
- 对每个核心,取前5个字段(user+nice+system+idle+iowait)求和得总活动时间,idle单独记为闲置时间
- 占用率 = (total_time - idle_time) / total_time * 100.0
- 两次采样间隔建议≥100ms,太短会导致精度崩坏(jiffies分辨率通常10ms)
- 不要依赖steal或guest字段,除非明确跑在虚拟机里且需要隔离统计
获取实时CPU频率得看/sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq
Linux内核暴露的scaling_cur_freq是当前实际运行频率(单位kHz),但要注意它不是硬件传感器读数,而是cpufreq驱动上报的“目标频率”——可能滞后于真实切换,尤其在boost场景下。
实操要点:
- 路径/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq存在才表示该核启用了cpufreq
- 读失败(如返回-ENODEV)说明该核被offline,或没加载cpufreq驱动(常见于嵌入式或容器环境)
- 某些ARM平台用cpuinfo_cur_freq替代,需fallback尝试
- 频率单位是kHz,别忘了除以1000转MHz;若读到0,大概率是权限不足(需root或sysfs可读)
- 不要轮询太快(
上下文切换次数只能从/proc/[pid]/status和/proc/stat交叉推算
/proc/[pid]/status里的voluntary_ctxt_switches和nonvoluntary_ctxt_switches是进程级累计值,但“趋势”需要时间维度。真正的难点在于:内核不提供每秒切换速率的直接接口,必须自己差分。
怎么做:
- 定期读取/proc/[pid]/status,提取两行:voluntary_ctxt_switches:和nonvoluntary_ctxt_switches:
- 同时读/proc/stat的ctxt字段(系统级总切换次数),用于校验异常突增(比如某进程切得太猛,但系统总量没变,可能是读错pid)
- 差分后除以时间间隔得“平均切换率(次/秒)”,但注意:单次差分噪声大,建议滑动窗口(如最近3次采样中位数)
- 如果nonvoluntary持续远高于voluntary,基本等于该进程在频繁阻塞(锁竞争、IO等待、缺页)
- 别试图用perf事件实时抓取——开销太大,且无法绑定到特定pid长期监控
C++实现时最容易踩的坑:文件重开、缓冲区溢出和权限静默失败
写代码时别以为std::ifstream读/proc路径很安全——实际会遇到一堆非典型错误:
立即学习“C++免费学习笔记(深入)”;
常见陷阱:
- /proc/[pid]/stat每行长度不固定,用std::getline可能截断(字段含空格的comm名),必须用std::istream::ignore跳过前13个字段再读第14个(utime)
- /proc/stat某些发行版(如Alpine)把cpu行放在最后,不能硬编码跳过1行
- 读/sys路径时,open()成功但read()返回0(文件为空),不是EOF,是驱动未就绪,得重试
- 没检查errno == EACCES就当文件不存在,结果在容器里跑失败却不报错
- 把std::stoull()用在可能含非数字字符的字段(如comm),导致抛异常中断采集
真正在生产环境跑,至少加一层retry+timeout封装,且所有/proc和/sys路径访问都得带access()预检。


















