最稳低开销的CPU占用率计算:Windows用GetSystemTimes(需注意权限、两次调用间隔≥100ms、FILETIME转微秒),Linux读/proc/stat首行并按字段语义解析,跨平台避免std::chrono直接减系统时间戳,采样间隔宜200–500ms。

Windows 上用 GetSystemTimes 算 CPU 占用率,别碰 Performance Counter
Windows 下最稳、最低开销的方式是调用 GetSystemTimes,它返回内核态、用户态和空闲时间的 64 位累计 tick 数。自己差分再除以总时间就能算出占用率。很多人一上来就查 Performance Counter(比如 "\Processor(_Total)\% Processor Time"),结果发现初始化慢、权限要求高、多线程下容易卡住——它本质是 COM 接口封装,不适合高频采样。
- 必须每轮都调用两次:一次取旧值,间隔几百毫秒后再取新值,否则差分为 0
- 两次调用之间不能小于 100ms,否则精度崩(系统只在特定中断点更新这些计数器)
-
GetSystemTimes要求进程有SE_INCREASE_QUOTA_NAME权限,但普通 GUI 进程默认就有,服务进程可能需显式提权 - 返回的
FILETIME是 100ns 单位,要转成毫秒得除以 10000;总时间 = 用户 + 内核 − 空闲
Linux 下读 /proc/stat 的第一行,别 parse 全部字段
Linux 没有统一 syscall 提供 CPU 占用率,/proc/stat 是唯一可靠来源。关键只看第一行 cpu 开头那条(含所有 CPU 总和),后面每行 cpu0、cpu1 是单核数据,除非你真要监控核级负载。
- 该行字段顺序固定:
user nice system idle iowait irq softirq steal guest guest_nice,但内核版本不同字段数可能变(如 2.6.33+ 加了guest),所以务必按空格 split 后取前 8–10 个数字,别硬写索引 - 计算时只用
user + nice + system + irq + softirq当“忙时间”,idle + iowait当“空闲时间”;steal和guest属于虚拟化开销,一般不计入主机 CPU 占用 - 读
/proc/stat是纯文本 I/O,频繁调用(如每 100ms)会触发 page cache 压力,建议用mmap或至少read配合固定 buffer 复用
跨平台封装时,避免用 std::chrono 直接减 FILETIME 或 timespec
Windows 的 FILETIME 和 Linux 的 timespec 都是纳秒/100ns 级时间戳,但 std::chrono::steady_clock::now() 在不同平台底层实现不同(Windows 是 QPC,Linux 是 clock_gettime(CLOCK_MONOTONIC)),直接拿它们跟系统时间戳做减法会出错。
- Windows 下把
FILETIME转成uint64_t(乘 10000 得微秒),再转std::chrono::microseconds构造time_point - Linux 下把
timespec的tv_sec * 1000000000ULL + tv_nsec当纳秒总数,再进std::chrono::nanoseconds - 千万别写
auto delta = now2 - now1然后直接除——类型不匹配会导致静默截断或负值
采样间隔低于 500ms 时,top 类工具的“瞬时值”其实是伪实时
所谓“当前 CPU 占用率”根本不存在——它永远是两个时间点之间的平均值。你看到的 73% 是过去 200ms 内的均值,不是此刻某一微秒的状态。很多监控程序设成 100ms 采样,结果曲线毛刺严重,误以为是波动大,其实是噪声放大。
立即学习“C++免费学习笔记(深入)”;
- 真实场景中,200–500ms 是平衡精度与稳定性的合理区间;低于 100ms 不仅没意义,还会因系统计数器更新延迟导致大量重复值
- 如果要做告警(比如 >90% 持续 5 秒),别对单次采样值判断,应维护一个滑动窗口(如最近 10 个值),检查是否连续 N 次超阈值
- 注意:虚拟机里
idle时间可能被宿主调度器“稀释”,导致算出的占用率虚高,这时得结合steal字段校正
事情说清了就结束











