Android 内核调度器在通用 Linux 调度器(CFS、RT、DL)的基础上,面临着三个独特的工程挑战:UI 线程的响应性保障、功耗与性能的动态平衡,以及多核异构(big.LITTLE)架构下的负载分配。这些挑战直接决定了用户对系统流畅度和续航的主观体验。
3.1 UI 线程调度:从“公平”到“响应优先”
通用 Linux CFS 调度器的核心目标是“公平”,即让每个就绪线程获得等比例的 CPU 时间。然而,Android 的 UI 渲染管线(Choreographer + SurfaceFlinger)对调度延迟极其敏感:一个 16.6ms 的垂直同步周期内,如果 UI 线程或 RenderThread 未能及时获得 CPU,就会导致掉帧(Jank)。
核心矛盾: CFS 的“公平时间片”分配模式,无法保证 UI 线程在 ms 级时间窗口 内的即时唤醒与执行。UI 线程需要的是“低延迟”而非“高吞吐”。
3.1.1 UI 线程的调度特征与识别
| 线程类型 | 调度特征 | 关键指标 | 调度器关注点 |
|---|---|---|---|
| UI Thread (主线程) | 短突发、高优先级、与 Input/VSYNC 强绑定 | 调度延迟 < 3ms | 优先唤醒、避免被后台任务抢占 |
| RenderThread | 与 GPU 同步、计算渲染命令 | 帧时间 < 16.6ms | 绑定到大核、避免 CPU 频率切换 |
| SurfaceFlinger | 合成图层、触发 HWC | 合成延迟 < 2ms | RT 优先级、独占 CPU 核心 |
| Binder Threads | IPC 通信、阻塞等待 | Binder 响应时间 | 避免优先级反转 |
3.1.2 关键调度机制:cgroup 与优先级映射
Android 通过 cgroup(控制组) 对 UI 相关线程进行分组管理,并配合 schedtune(或新版内核的 uclamp)机制,向调度器传递“性能偏好”提示。
# 查看 UI 相关线程的 cgroup 分组 adb shell cat /dev/cpuset/top-app/tasks # 输出示例:包含当前前台应用的 UI 线程、RenderThread 等 # 查看 schedtune 的 boost 值(旧版内核) adb shell cat /dev/stune/top-app/schedtune.boost # 输出:10 (表示轻度 boost,倾向于大核) # 查看 uclamp 值(新版内核,4.19+) adb shell cat /sys/fs/cgroup/cpu/top-app/cpu.uclamp.latency_sensitive # 输出:1 (标记为延迟敏感)调优实践: 在自定义内核中,可以通过修改 schedtune.boost 的默认值(0~100)来调整 UI 线程的“大核倾向性”。但过高的 boost 会导致小核空闲、功耗激增。推荐值为 10~30。
3.2 功耗与性能平衡:EAS 调度器的核心博弈
Android 从内核 4.14 开始全面引入 EAS(Energy-Aware Scheduling,能耗感知调度),其核心思想是:在保证性能的前提下,选择能耗最低的 CPU 核心执行任务。EAS 的决策依赖于两个关键模型:CPU 的能耗模型(Energy Model, EM) 和 任务的性能需求模型(Task Utilization)。
3.2.1 EAS 的调度决策流程
- 任务利用率追踪: PELT(Per-Entity Load Tracking)算法追踪每个任务的 CPU 利用率(util_avg),范围 0~1024。
- CPU 容量评估: 每个 CPU 核心的“最大容量”(capacity)由频率和微架构决定。大核容量通常为 1024,小核为 400~600。
- 能耗计算: 对于每个可能的 CPU,EAS 计算“预估能耗 = 任务利用率 × 该 CPU 的每单位利用率能耗”。
- 选择最优 CPU: