云计算百科
云计算领域专业知识百科平台

Android CPU 使用率采集:/proc/stat 计算公式与完整方案(附实例)

做 Android 性能测试,CPU 使用率是最基础的指标。但很多人只会用 top 看一眼,对背后的数据源和计算逻辑并不清楚。 本篇从概念讲起,先给公式、再看数据源、最后用真实数据走一遍完整计算。


一、CPU 使用率到底在度量什么

先明确一个概念:CPU 使用率衡量的是"时间片的分配比例",不是"算力的消耗比例"。

Linux 内核用一个叫 tick 的时间片来调度任务。每个 tick 大约 4~10 毫秒(取决于内核配置 CONFIG_HZ)。每个 tick 结束时,内核记录这个 tick 被分配到了哪个状态:用户态、内核态、空闲、等待 IO……

一句话定义:

CPU 使用率 = 一段时间内,非空闲 tick 占总 tick 的比例


二、核心公式(先看结论)

CPU 统计数据都是从开机开始的累积值,所以必须采两次、取差值才能算出一段时间内的使用率。

2.1 整机 CPU 使用率

第 1 次采样 → total₁, idle₁
等待 N 秒
第 2 次采样 → total₂, idle₂

Δtotal = total₂ − total₁ ← 这段时间所有核心总共产生了多少 tick
Δidle = idle₂ − idle₁ ← 其中有多少 tick 是空闲的

整机 CPU% = (Δtotal − Δidle) / Δtotal × 100

其中:

变量含义怎么得到
total 所有状态 tick 之和 user + nice + system + idle + iowait + irq + softirq + steal
idle 空闲 tick /proc/stat 第一行的第 4 个数值字段

2.2 进程 CPU 使用率

第 1 次采样 → process_time₁(同时采整机 total₁)
等待 N 秒
第 2 次采样 → process_time₂(同时采整机 total₂)

Δprocess = process_time₂ − process_time₁ ← 进程自己消耗的 tick 增量
Δtotal = total₂ − total₁ ← 整机总 tick 增量(同上)

进程 CPU% = Δprocess / Δtotal × 100

其中:

变量含义怎么得到
process_time 进程累积的 CPU tick utime + stime + cutime + cstime
Δtotal 整机总 tick 增量 和整机公式用同一个分母

关键点:进程 CPU% 的分母是整机的 Δtotal,不是进程自己的数据。

含义是"这个进程在所有 CPU 资源中占了多少比例"。4 核设备上,单线程进程最高约 25%,4 线程跑满接近 100%。

2.3 公式小结

整机 CPU% = (Δtotal − Δidle) / Δtotal × 100

进程 CPU% = Δprocess / Δtotal × 100

就这两个公式,所有 CPU 采集工具(top、dumpsys cpuinfo、各种 APM SDK)底层都是这个逻辑。


三、数据源:数据从哪来

3.1 整机数据:/proc/stat 第一行

Linux 内核提供的 CPU 统计接口,所有 Android 设备都有。读取后第一行格式如下:

cpu 10132153 290696 3084719 46828483 16683 0 25195 0 0 0

跳过开头的 cpu,后面 8 个数值依次是:

位置名称含义
1 user 用户态时间(App 代码执行)
2 nice 低优先级用户态时间
3 system 内核态时间(系统调用、驱动)
4 idle 空闲时间(CPU 在发呆)
5 iowait 等待 IO 的空闲时间(等磁盘/网络)
6 irq 硬中断处理时间
7 softirq 软中断处理时间
8 steal 虚拟化被偷走的时间

total = 上面 8 个字段全部相加。

3.2 汇总行 vs 各核:用哪一行

/proc/stat 的完整输出中,第一行下面还有每个核心的单独数据:

cpu 10132153 290696 3084719 46828483 16683 0 25195 0 0 0 ← 汇总行(我们用这行)
cpu0 2503274 72633 771347 11709498 4180 0 6313 0 0 0 ← 核心 0
cpu1 2522508 73222 770877 11707883 4115 0 6239 0 0 0 ← 核心 1
cpu2 2505698 72460 771276 11706498 4198 0 6312 0 0 0 ← 核心 2
cpu3 2514673 72381 771219 11704604 4190 0 6331 0 0 0 ← 核心 3

汇总行 = 所有在线核心的累加。以 idle 字段验证:

cpu0.idle + cpu1.idle + cpu2.idle + cpu3.idle
= 11709498 + 11707883 + 11706498 + 11704604
= 46828483
= 汇总行的 idle ✓

所以:

问题答案
需要自己加各核数据吗? 不需要,内核已经帮我们加好了
直接用汇总行就行? 是的,只取第一行 cpu
关核了怎么办? 关掉的核不出现在输出中,汇总行只含在线核
4 核 total 每秒增加多少? 约 4 × HZ(HZ=100 时约 400 tick/秒)

3.3 进程数据:/proc/{pid}/stat

读取目标进程的 stat 文件,输出是一行很长的数据:

12345 (com.example.app) S 1 12345 12345 0 -1 … 5000 1200 0 0 …

我们关心的是第 14~17 个字段(从 1 开始计数):

字段号名称含义
14 utime 进程在用户态消耗的 tick
15 stime 进程在内核态消耗的 tick
16 cutime 已退出子进程的用户态 tick(累积)
17 cstime 已退出子进程的内核态 tick(累积)

process_time = utime + stime + cutime + cstime


四、完整计算实例

用一组真实格式的数据,从头到尾走一遍。

4.1 第 1 次采样

读 /proc/stat 第一行:

cpu 10132153 290696 3084719 46828483 16683 0 25195 0 0 0

解析:

user=10132153 nice=290696 system=3084719 idle=46828483
iowait=16683 irq=0 softirq=25195 steal=0

total₁ = 10132153 + 290696 + 3084719 + 46828483 + 16683 + 0 + 25195 + 0
= 60,377,929

idle₁ = 46,828,483

同时读进程 /proc/{pid}/stat:

utime=5000 stime=1200 cutime=0 cstime=0

process_time₁ = 5000 + 1200 + 0 + 0 = 6,200

4.2 等待 10 秒,第 2 次采样

读 /proc/stat 第一行:

cpu 10132953 290720 3085019 46832083 16690 0 25210 0 0 0

解析:

total₂ = 10132953 + 290720 + 3085019 + 46832083 + 16690 + 0 + 25210 + 0
= 60,382,675

idle₂ = 46,832,083

同时读进程 /proc/{pid}/stat:

utime=5350 stime=1280 cutime=0 cstime=0

process_time₂ = 5350 + 1280 + 0 + 0 = 6,630

4.3 算差值

字段第 1 次第 2 次差值(Δ)
user 10,132,153 10,132,953 800
nice 290,696 290,720 24
system 3,084,719 3,085,019 300
idle 46,828,483 46,832,083 3,600
iowait 16,683 16,690 7
irq 0 0 0
softirq 25,195 25,210 15
steal 0 0 0
total 60,377,929 60,382,675 4,746

为什么 10 秒产生了 4746 个 tick?因为 4 核设备,每个核每秒约产生 HZ 个 tick(HZ≈100~120),4 × ~119 × 10 ≈ 4746。

4.4 代入公式

整机 CPU%:

Δtotal = 4,746
Δidle = 3,600

整机 CPU% = (Δtotal − Δidle) / Δtotal × 100
= (4746 − 3600) / 4746 × 100
= 1146 / 4746 × 100
≈ 24.1%

→ 过去 10 秒,4 个核心总共有 24.1% 在干活,75.9% 在空闲。

细分一下各状态占比:

user 占比 = 800 / 4746 × 100 = 16.9% ← 用户态(App 代码)
system 占比 = 300 / 4746 × 100 = 6.3% ← 内核态(系统调用)
iowait 占比 = 7 / 4746 × 100 = 0.1% ← 等 IO(几乎没有)
nice 占比 = 24 / 4746 × 100 = 0.5%
softirq占比 = 15 / 4746 × 100 = 0.3%

进程 CPU%:

Δprocess = 6630 − 6200 = 430

进程 CPU% = Δprocess / Δtotal × 100
= 430 / 4746 × 100
≈ 9.1%

→ 这个进程在过去 10 秒,消耗了整机 CPU 资源的 9.1%。

4.5 一张图看全貌

Δtotal = 4,746 tick(10 秒 × 4 核)
┌─────────────────────────────────────────────────┐
│ │
│ ┌── user = 800 ─┐ │
│ ├── nice = 24 │ │
│ ├── system = 300 ├─ 忙碌 = 1,146 (24.1%) │
│ ├── iowait = 7 │ │
│ ├── softirq = 15 │ │
│ ├── irq = 0 ┘ │
│ │ │
│ └── idle = 3,600 ── 空闲 (75.9%) │
│ │
│ 其中进程 com.example.app 消耗 430 tick = 9.1% │
└─────────────────────────────────────────────────┘


五、常见采集方式对比

除了直接读 /proc/stat,还有几种常见方式,它们底层都在用同样的公式:

维度直接读 /proc/stattop -n 1dumpsys cpuinfo
底层原理 就是原始数据源 内部读 /proc/stat 帮你算 通过 Binder 向 system_server 取
数据精度 最高(原始 tick) 中(top 自己算的) 低(系统统计窗口不精确)
采集开销 最低(2~3ms) 中(50~200ms) 最高(100~500ms)
需要自己算
输出格式稳定 是(内核标准格式) 差(各版本不同)
适合高频采集 不推荐
适合手动看一眼 否(原始数字)

建议:写采集工具 → 直接读 /proc/stat;手动快速看 → 用 top 或 dumpsys cpuinfo。


六、采集间隔与精度

6.1 间隔选择

间隔适用场景注意事项
1~2 秒 短时间精细分析(启动、页面切换) ADB 采集开销占比高,建议设备端脚本
5~10 秒 长时间压测(推荐) 平衡精度和开销
30~60 秒 粗粒度监控 可能漏掉短暂的 CPU 飙升

常用选择是 10 秒——24 小时压测产生 8640 个数据点,够做趋势分析,又不会干扰设备。

6.2 tick 精度:CONFIG_HZ

/proc/stat 里的数值单位是 tick。每个 tick 的时长取决于内核编译时的 CONFIG_HZ:

CONFIG_HZ每 tick 时长常见平台
100 10ms 部分旧内核
250 4ms MTK 平台常见
300 3.3ms 部分高通平台
1000 1ms 服务器/桌面 Linux

可通过 adb shell getconf CLK_TCK 查看设备的 HZ 值。

HZ=100 时,一个运行了 5ms 的短任务可能不会被记录(不到 1 tick)。对大部分性能测试场景(间隔 ≥ 5 秒),这个精度限制可以忽略。


七、完整采集流程

把前面的内容串起来,一次完整的 CPU 采集分三步:

┌───────────────────────────────────────────────────────┐
│ Step 1:第 1 次采样 │
│ │
│ · 读 /proc/stat → 取第一行 │
│ → 解析 8 个字段 │
│ → total₁ = 8 个字段之和 │
│ → idle₁ = 第 4 个字段 │
│ │
│ · 读 /proc/{pid}/stat │
│ → 取第 14~17 字段 │
│ → process_time₁ = utime + stime + cutime + cstime │
└───────────────────────────────────────────────────────┘

│ 等待 N 秒(推荐 10 秒)

┌───────────────────────────────────────────────────────┐
│ Step 2:第 2 次采样(同样步骤) │
│ │
│ → total₂, idle₂, process_time₂ │
└───────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────┐
│ Step 3:代入公式 │
│ │
│ Δtotal = total₂ − total₁ │
│ Δidle = idle₂ − idle₁ │
│ Δprocess = process_time₂ − process_time₁ │
│ │
│ 整机 CPU% = (Δtotal − Δidle) / Δtotal × 100 │
│ 进程 CPU% = Δprocess / Δtotal × 100 │
└───────────────────────────────────────────────────────┘


小结

知识点一句话
CPU% 的本质 非空闲 tick 占总 tick 的比例
核心公式 整机 (Δtotal−Δidle)/Δtotal,进程 Δprocess/Δtotal
整机数据源 /proc/stat 第一行(汇总行,不需要手动加各核)
进程数据源 /proc/{pid}/stat 第 14~17 字段
total 怎么算 汇总行 8 个数值字段全部相加
推荐采集方式 直接读 /proc/stat,不用 top / dumpsys
推荐采集间隔 10 秒(长时间压测)
精度上限 CONFIG_HZ 决定,通常 4~10ms

掌握了这些基础,你就能看懂任何 CPU 采集工具的原理了。下一篇我们讲这个"简单公式"里藏着的 8 个坑——每个都可能让你的数据严重失真。

提示:本篇讲的 CPU% 衡量的是"时间片占比"(Raw CPU%)。设备降频时,同样的 CPU% 对应的实际算力会大幅缩水。这个问题以及对应的 Normalized CPU% 方案,会在第 7 篇和第 8 篇展开。


系列目录

  • 第 1 篇:内存泄漏自动检测(上)——采集层设计
  • 第 2 篇:内存泄漏自动检测(中)——检测层设计
  • 第 3 篇:内存泄漏自动检测(下)——响应层设计
  • 第 4 篇:Android 内存采集避坑指南
  • 第 5 篇(本篇):Android CPU 使用率采集入门
  • 第 6 篇(下一篇):CPU 采集的 8 个坑
  • 第 7 篇:CPU 降频了,你的采集数据还准吗?
  • 第 8 篇:CPU 单核分析与归一化方案

我是测试工坊,专注 Android 系统级性能工程。 如果你也在做 CPU 相关的性能测试,欢迎评论区交流 👇 关注我,后续更新不迷路。

赞(0)
未经允许不得转载:网硕互联帮助中心 » Android CPU 使用率采集:/proc/stat 计算公式与完整方案(附实例)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!