2. Android系统架构与CPU基础

要做好CPU调度与功耗优化,第一步不是急着改参数,而是建立一张清晰的“系统地图”:应用代码从哪一层开始,经过哪些软件栈,最终落到哪颗CPU核心、以什么频率和电压运行。只有把Android软件栈、Linux内核、CPU微架构、核心与簇、频率与电压、性能与功耗这几层关系串起来,后续的调度策略、绑核、限频、温控才有落脚点。

一、Android软件栈:从应用到硅片的纵向分层

Android是一个典型的分层系统。最上层是应用层,包括系统应用与第三方App;其下是Java API框架层(Framework),提供ActivityManager、WindowManager等服务;再往下是原生库与Android运行时(ART),负责图形、媒体、数据库以及字节码执行;最底层是Linux内核,包含进程调度、内存管理、电源管理、驱动等。CPU调度与功耗优化主要发生在内核层,但触发者往往来自上层。

理解这条纵向链路的意义在于:上层的一次滑动、一次视频解码、一次后台同步,最终都会转化为内核中的任务(task)和线程(thread),由调度器决定它们在哪个CPU上运行、运行多久。功耗问题也往往不是单层造成的,而是“上层频繁唤醒 + 内核调度不当 + 硬件高频运行”共同作用的结果。

二、Linux内核:调度的真正执行者

Linux内核是Android与硬件之间的桥梁。与CPU调度和功耗直接相关的子系统包括:进程调度器(CFS、实时调度、EAS等)、CPU频率调节框架(cpufreq)、CPU空闲管理(cpuidle)、热管理(thermal)、以及电源管理(PM)。内核通过sysfs和procfs向用户空间暴露大量可观测、可配置的节点。

# 查看当前CPU频率与调度信息
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
cat /proc/cpuinfo

# 查看各核心负载与运行状态
top -H -p <pid>
cat /proc/<pid>/task/<tid>/stat

这些节点是后续调优的“仪表盘”。例如scaling_governor决定频率如何随负载变化,cpuinfo反映核心拓扑,task/stat中的utime、stime则帮助判断线程是计算密集还是等待唤醒。

三、CPU微架构、核心与簇

现代移动SoC普遍采用异构多核架构,即big.LITTLE或DynamIQ。核心被划分为若干簇(cluster),同一簇内核心通常共享频率域和电压域,簇间可以独立调频。典型结构是“超大核 + 大核 + 小核”三簇,分别面向瞬时爆发、持续性能和低功耗后台任务。

微架构决定了单核的每时钟周期指令数(IPC)、流水线深度、缓存层次和分支预测能力。同一频率下,不同微架构的算力与功耗差异很大。因此“频率高”不等于“性能好”,“核心多”也不等于“更省电”。调度器需要根据任务特性,把重负载放到大核、把轻负载或后台任务压到小核,才能兼顾响应与续航。

Android软件栈 → 内核调度 → CPU核心簇 → 频率/电压 → 性能与功耗 应用层 / App Framework / Java API Native库 / ART运行时 Linux内核 驱动 / 硬件抽象 内核调度器 CFS / RT / EAS cpufreq / cpuidle thermal / PM 任务放置 · 频率选择 异构多核 CPU(核心与簇) 超大核 Prime 大核 Gold 小核 Silver 簇内共享频率域 / 电压域,簇间独立调频 微架构决定 IPC、缓存与能效曲线 频率 f 电压 V P ∝ C·V²·f 动态功耗随V²增长 性能与功耗的权衡 性能 ≈ IPC × 频率 × 并行度 功耗 ≈ 静态漏电 + 动态开关功耗 目标:单位功耗下的有效算力最大化

四、频率与电压:功耗的非线性来源

CPU的动态功耗近似满足 P ∝ C·V²·f,其中C为等效电容,V为工作电压,f为频率。频率提升往往需要同步提高电压才能稳定,因此功耗随频率呈超线性增长。这也是为什么“把频率拉满”在移动端通常得不偿失:性能可能只提升20%,功耗却可能翻倍。

内核通过OPP(Operating Performance Point)表管理频率与电压的对应关系。调频策略(governor)决定何时升频、何时降频。常见governor包括performance、powersave、schedutil、ondemand等。现代Android更倾向使用schedutil,因为它能结合调度器的负载信息做更及时的频率决策。

层级/对象 关注点 典型观测或配置
应用/Framework 任务频率、唤醒次数、线程优先级 Trace、Binder调用、Handler消息
Linux内核 调度策略、频率选择、空闲状态 schedutil、cpuidle、thermal
CPU核心/簇 任务放置、负载均衡、簇间迁移 EAS、core_ctl、cpuinfo
频率/电压 OPP、电压轨、功耗曲线 scaling_cur_freq、regulator
性能/功耗 能效比、温控降频、续航 PowerProfiler、能耗模型

五、案例与场景:一次滑动背后的调度链路

假设用户滑动列表,UI线程需要在一帧16.6ms内完成测量、布局和绘制。若此时小核正在处理后台同步,调度器可能把UI线程放到大核,并迅速提升该簇频率;绘制完成后负载下降,频率回落。若后台任务持续占用大核,就会挤压UI线程,造成掉帧和额外功耗。

优化思路:先定位“谁在跑、跑在哪、跑多快”,再决定是限制后台、调整线程优先级,还是修改调度/频率策略。不要一上来就锁大核或锁高频。

六、常见误区与复盘

  • 误区一:频率越高越流畅。 高频带来功耗和发热,发热后触发温控降频,反而可能更卡。
  • 误区二:核心越多越好。 多核唤醒会增加漏电和调度开销,轻负载下应尽量合并到少量核心。
  • 误区三:只看平均功耗。 瞬时功耗尖峰同样影响续航和温控,需要结合Trace看时间分布。
  • 误区四:忽略电压域。 同簇共享电压,一个核心升频可能抬高整簇功耗。
复盘方法:用perfetto或systrace抓取一次典型场景,对齐CPU频率、核心运行、线程状态和帧时间四条泳道,找出“高负载—高频—高功耗”之间的因果链,再针对性优化。

本节的核心结论是:Android软件栈决定任务从哪来,Linux内核决定任务去哪跑,CPU微架构与核心簇决定跑得有多快,频率与电压决定代价有多大,而性能与功耗的平衡,正是CPU调度与功耗优化工程要持续求解的问题。