linux下计算每核占用率需结合/proc/[pid]/task/[tid]/stat线程级时间与/proc/stat各cpu行总时间戳,两次采样间隔至少100ms;windows无原生per-core接口,须绑核后配合querythreadcycletime等api估算,第三方库多为均分式估算而非真实分布。

Linux 下用 /proc/[pid]/stat 和 /proc/stat 算每核占用率
直接读 /proc/[pid]/stat 只能拿到进程总时间(utime + stime),没法拆到每个逻辑核。真要分核统计,得结合线程级信息:/proc/[pid]/task/[tid]/stat 里每个线程有自己的运行时间,再配合 /proc/stat 中各 CPU 的总时间戳,才能算出每个核上该进程(及其线程)的占用比例。
关键点是:同一时刻采集所有线程的 utime/stime,同时读取 /proc/stat 每行以 cpu[0-9]+ 开头的记录(注意跳过 cpu 总和行)。每行第 1–8 列是各类型时间(单位:jiffies),求和得该核总调度时间。
- 先遍历
/proc/self/task/目录获取所有tid - 对每个
tid,读/proc/self/task/[tid]/stat,提取第 14(utime)、15(stime)字段 - 读
/proc/stat,按cpu\d+行解析,累加user、nice、system、idle、iowait、irq、softirq得总 jiffies - 两次采样间隔至少 100ms,否则差值为 0 或噪声大
pthread_getcpuclockid 不适合实时每核统计
这个函数返回的是线程的 CPU 时间时钟 ID,调用 clock_gettime 可得该线程自启动以来的总 CPU 时间(纳秒级),但它不区分物理核或逻辑核——它只是个累加器,无法告诉你“此刻在哪个核上跑”或“过去 1 秒在 core 2 上用了多少时间”。想靠它反推每核占用率,没底层调度器暴露的 per-core runtime 数据,基本不可行。
常见误用是:对每个线程调一次 clock_gettime,然后除以总时间,以为得到“该线程在某核占比”。错在:线程可能在多个核间迁移,而 clock 返回的是累计值,不是瞬时分布。
-
pthread_getcpuclockid返回的时钟只反映时间总量,无空间(核)维度 - Linux 调度器不向用户态暴露 per-CPU 的线程驻留时间映射
- 若强行绑定线程到单核(
pthread_setaffinity_np),再用该 clock 做差值,也只能估算该核上的贡献,但无法排除其他线程干扰
Windows 上必须用 QueryProcessCycleTime + QueryThreadCycleTime 配合 GetLogicalProcessorInformation
Windows 没有类似 /proc 的文件系统接口,得依赖 WinAPI。核心思路是:先获取当前进程所有线程 ID,对每个线程调 QueryThreadCycleTime(返回该线程在所有核上消耗的周期数),再用 QueryProcessCycleTime 验证一致性;但依然不能直接分核——因为 cycle time 是全局累加值。
真正能接近每核粒度的方式,是结合 GetLogicalProcessorInformation 获取拓扑,再用 QueryUnbiasedInterruptTime 和 QueryInterruptTime 做时间基线,配合 SetThreadIdealProcessor 强制绑核后单独测量。但这属于主动干预,不是被动观测。
- 默认情况下,Windows 不提供“某进程在 core 3 上过去 1s 占用了多少 ms”的 API
-
QueryThreadCycleTime返回的是总周期数,需自行换算成毫秒(除以GetTickCount64频率或用QueryPerformanceFrequency) - 若未显式绑核,线程可能跨核调度,cycle time 无法归属到具体核
别信“一个函数搞定”的第三方库封装
很多 C++ 封装库(比如某些 psutil 的 C++ 移植版、libstatgrab)对外声称支持“per-core CPU usage”,实际底层仍是采样 /proc/[pid]/stat 或 Windows 的 GetSystemTimes,然后按线程数平均分配,或简单把进程总占用均分到逻辑核数。这不是真实 per-core 占用,而是估算。
真实场景中,一个 4 线程进程可能全挤在 core 0 上跑满,其余 core 闲着——这时均分结果会严重失真。只有你自己控制采样节奏、线程遍历、核时间戳对齐,才能逼近真实分布。
- 检查库源码,确认它是否真的读
/proc/self/task/*/stat并关联/proc/stat的 cpuN 行 - 避免使用只调一次
getrusage或clock_gettime(CLOCK_PROCESS_CPUTIME_ID)的实现 - 跨平台库往往在 Windows 上退化为“进程总占用 / 核数”,Linux 上才可能做线程级拆解
线程与核的映射是动态的,采样窗口、调度延迟、jiffies 精度都会影响结果。拿不准的时候,先用 top -H -p [pid] 看线程级负载,再对比你程序输出——如果差异超过 15%,大概率是采样不同步或没过滤掉内核线程(如 kthreadd 子线程)。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











