最可靠方法是两次采样/proc/[pid]/stat的utime+stime与/proc/stat的系统总jiffies,计算差值占比;分母应为逻辑核数×采样间隔或系统jiffies增量,而非开机时间。

Linux下用 /proc/[pid]/stat 解析 CPU 时间最可靠
直接读取 /proc/[pid]/stat 是 Linux 上获取进程 CPU 占用率的底层方式,比 shell 命令或库封装更轻、更准、无额外进程开销。关键不是“读”,而是怎么算——CPU 占用率本质是「该进程在采样周期内占用的 CPU 时间占比」,必须做两次采样并结合系统总 jiffies 计算。
- 从
/proc/[pid]/stat第 14、15 字段(utime和stime)提取用户态 + 内核态总 tick 数;注意字段顺序固定,但需跳过括号内的命令名(可能含空格) - 同时读
/proc/stat的第一行(cpu),累加前 4 个数字得到系统总 jiffies(user+nice+system+idle,不含 iowait 等) - 两次采样间隔建议 ≥200ms:太短会导致差值为 0;太长则实时性下降,且需注意
jiffies是 uint64,但内核每秒更新频率有限(通常 100–1000Hz) - 别用
clock_gettime(CLOCK_PROCESS_CPUTIME_ID):它只返回本进程时间,无法和系统总时间对齐,算不出百分比
Windows 上必须用 GetProcessTimes() + 系统 uptime 差值
Windows 没有类似 /proc 的实时文件接口,GetProcessTimes() 返回的是 FILETIME(100ns 精度),但单次调用只能得累计值。要换算成占用率,必须和系统启动后总运行时间(uptime)联动计算。
- 调用
GetProcessTimes()得到kernelTime和userTime,合并为 total CPU time(单位:100ns) - 用
GetTickCount64()获取系统已运行毫秒数,再乘以 10000 转为 100ns 单位(⚠️注意:不能直接用GetSystemTimeAsFileTime(),那是绝对时间,不是 uptime) - 两次采样间,进程 CPU 时间差 ÷ 系统 uptime 差 = 近似占用率;若进程被挂起,这个值会偏低,属正常行为
- 权限问题常被忽略:对非自身进程调用
OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION)就够了,无需PROCESS_QUERY_INFORMATION(后者需要调试权限)
跨平台封装时,避免用 std::this_thread::sleep_for() 控制采样节奏
睡眠精度受系统调度影响极大:Linux 默认 timer resolution 是 10–15ms,Windows 可能更差。用 sleep_for(200ms) 实际可能停 230ms 或 280ms,导致分母(系统时间差)不准,最终 CPU% 波动剧烈甚至超 100%。
- 改用单调时钟主动轮询:记录上一次采样时刻(
std::chrono::steady_clock::now()),每次循环检查是否 ≥目标间隔,未到就sleep_for(1ms)再试 - 不要把两次
utime+stime直接相减当作“本次 CPU 时间”:如果进程刚被调度、还没执行完当前时间片,差值可能远小于真实消耗 - 多线程进程的 CPU 时间是所有线程之和,
/proc/[pid]/stat和GetProcessTimes()都已包含,无需额外遍历线程
常见错误:top 显示 120% 但你的程序算出 95%,不是代码错
这是采样窗口和统计口径差异导致的。例如 top 默认每 3 秒刷新、显示最近 3 秒平均,而你可能按 1 秒间隔采样并立即计算——前者平滑,后者敏感。更关键的是:top 的 %CPU 是「相对于单个逻辑核」的,120% 表示它平均占用了 1.2 个核;而你的公式如果除以「系统总核数 × 时间」,结果自然偏低。
立即学习“C++免费学习笔记(深入)”;
- 想对齐
top行为?把分母改成「单核时间 × 采样间隔」:即delta_process_jiffies / (sysconf(_SC_CLK_TCK) * interval_sec) - macOS 不适用本文方法:它没有
/proc,需用libproc+task_info(),且返回的是毫秒级近似值,精度和语义都不同 - 容器环境(如 Docker)中,
/proc/[pid]/stat仍有效,但系统总 jiffies 来自宿主机,而进程受 cgroup CPU quota 限制——此时你的百分比反映的是「在配额内跑满的程度」,不是物理核占用
实际写的时候,最易被绕进去的是“该拿什么当分母”。不是系统总时间,不是开机时间,而是“这段时间里,整个系统理论上最多能提供多少 CPU 时间”——它等于(逻辑核数 × 采样间隔),或者等价于(系统总 jiffies 增量)。少盯一眼,结果就飘了。










