Android CPU调度优化完整剖析指南
1. CPU调度基础概念
1.1 什么是CPU调度?
CPU调度是操作系统内核的核心功能,负责决定哪个进程/线程在何时使用哪个CPU核心。在Android系统中,良好的调度策略直接影响用户体验、性能和功耗。
1.2 关键术语解释
- 进程(Process): 程序的运行实例
- 线程(Thread): 进程内的执行单元
- 任务(Task): Linux内核中的调度实体
- 调度器(Scheduler): 负责调度决策的内核组件
- 时间片(Time Slice): 任务连续执行的时间长度
- 上下文切换(Context Switch): CPU从一个任务切换到另一个任务
1.3 Android调度架构层次
┌─────────────────────────────────────┐
│ 应用层 (Applications) │
├─────────────────────────────────────┤
│ 框架层 (Android Framework) │
├─────────────────────────────────────┤
│ 系统调用层 (System Calls) │
├─────────────────────────────────────┤
│ 内核调度层 (Kernel Scheduler)│
├─────────────────────────────────────┤
│ 硬件抽象层 (HAL) │
├─────────────────────────────────────┤
│ 硬件层 (Hardware) │
└─────────────────────────────────────┘
2. 硬件层CPU调度原理
2.1 ARM多核架构基础
现代Android设备通常采用ARM big.LITTLE架构:
- 大核(Big cores): 高性能,高功耗
- 小核(LITTLE cores): 低性能,低功耗
- DynamIQ技术: 允许不同核心共享缓存
2.2 CPU拓扑结构
c
// 内核中的CPU拓扑表示
struct cpu_topology {
int thread_id; // 线程ID
int core_id; // 核心ID
int package_id; // 封装ID
int llc_id; // 最后级缓存ID
};
2.3 硬件性能状态
- P-states: 性能状态,调节CPU频率
- C-states: 睡眠状态,CPU空闲时的省电模式
- DVFS: 动态电压频率调节
3. 内核层调度器实现
3.1 Linux调度器演进
- O(1)调度器: Linux 2.6之前的调度器
- CFS调度器: 完全公平调度器(当前主要调度器)
- EAS调度器: 能量感知调度器(Android特有优化)
3.2 CFS调度器核心原理
3.2.1 虚拟运行时间
c
struct sched_entity {
u64 vruntime; // 虚拟运行时间
u64 exec_start; // 开始执行时间
u64 sum_exec_runtime; // 总执行时间
u64 prev_sum_exec_runtime; // 上次执行时间
};
3.2.2 红黑树调度队列
CFS使用红黑树管理就绪队列,键值为虚拟运行时间:
- 最左节点:vruntime最小的任务(下一个运行的任务)
- 插入复杂度:O(log n)
- 删除复杂度:O(log n)
3.3 调度类(Scheduling Classes)
3.3.1 实时调度类(RT)
c
struct sched_rt_entity {
struct list_head run_list; // 运行列表
unsigned long timeout; // 超时时间
unsigned int time_slice; // 时间片
int nr_cpus_allowed; // 允许的CPU数
};
3.3.2 完全公平调度类(CFS)
c
struct sched_entity {
struct load_weight load; // 负载权重
struct rb_node run_node; // 红黑树节点
unsigned int on_rq; // 是否在运行队列
u64 vruntime; // 虚拟运行时间
};
3.3.3 截止时间调度类(DL)
c
struct sched_dl_entity {
u64 dl_runtime; // 运行时间
u64 dl_deadline; // 截止时间
u64 dl_period; // 周期
u64 dl_bw; // 带宽
};
3.4 负载计算与PELT算法
3.4.1 PELT(Per-Entity Load Tracking)
c
struct sched_avg {
u64 last_update_time; // 最后更新时间
u64 load_sum; // 负载总和
u32 load_avg; // 平均负载
u32 util_avg; // 平均利用率
u32 util_est; // 利用率估计
};
3.4.2 负载衰减计算
c
static __always_inline u64 decay_load(u64 val, u64 n) {
// 指数衰减计算
return val >> n;
}
3.5 CPU容量感知调度
3.5.1 CPU容量计算
c
#define SCHED_CAPACITY_SHIFT 10
#define SCHED_CAPACITY_SCALE (1L << SCHED_CAPACITY_SHIFT)
static inline unsigned long capacity_of(int cpu) {
return cpu_rq(cpu)->cpu_capacity;
}
3.5.2 容量约束条件
c
#define fits_capacity(cap, max) ((cap) * 1280 < (max) * 1024)
4. Android框架层调度接口
4.1 Process类调度接口
java
public class Process {
// 设置线程优先级
public static final int setThreadPriority(int tid, int priority);
// 获取线程优先级
public static final int getThreadPriority(int tid);
// 设置线程调度策略
public static final int setThreadScheduler(int tid, int policy, int priority);
}
4.2 ActivityManager调度控制
java
public class ActivityManager {
// 设置进程重要性
public void setProcessImportant(ComponentName component, int importance);
// 获取内存信息
public void getMemoryInfo(MemoryInfo outInfo);
}
4.3 线程优先级定义
java
public class Process {
// 线程优先级常量
public static final int THREAD_PRIORITY_DEFAULT = 0;
public static final int THREAD_PRIORITY_LOWEST = 19;
public static final int THREAD_PRIORITY_BACKGROUND = 10;
public static final int THREAD_PRIORITY_FOREGROUND = -2;
public static final int THREAD_PRIORITY_DISPLAY = -4;
public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8;
}
5. 调度策略与算法实现
5.1 负载均衡算法
5.1.1 负载均衡触发条件
- 周期性负载均衡(定时器触发)
- 任务唤醒时负载均衡
- CPU空闲时主动拉取任务
5.1.2 负载均衡实现
c
static int load_balance(int this_cpu, struct rq *this_rq,
struct sched_domain *sd, enum cpu_idle_type idle) {
// 1. 找到最繁忙的调度组
// 2. 计算需要迁移的负载量
// 3. 选择要迁移的任务
// 4. 执行任务迁移
}
5.2 任务唤醒优化
5.2.1 快速路径选择
c
static int select_task_rq_fair(struct task_struct *p, int prev_cpu,
int sd_flag, int wake_flags) {
// 1. 检查当前CPU是否合适
// 2. 查找空闲CPU
// 3. 容量感知CPU选择
// 4. 缓存亲和性考虑
}
5.2.2 缓存亲和性优化
- SMT(超线程): 优先选择同核心的兄弟CPU
- 共享缓存: 优先选择共享L2/L3缓存的CPU
- NUMA节点: 优先选择本地NUMA节点
5.3 能量感知调度(EAS)
5.3.1 能量模型
c
struct energy_model {
struct em_perf_domain *pd; // 性能域
unsigned int nr_perf_domains; // 性能域数量
unsigned long *power_table; // 功耗表
};
5.3.2 能量计算
c
static unsigned long compute_energy(struct task_struct *p, int dst_cpu) {
// 1. 计算目标CPU的能量消耗
// 2. 考虑任务迁移的能量开销
// 3. 选择能量最优的CPU
}
6. 性能监控与调试
6.1 调度统计信息
6.1.1 运行队列统计
c
struct rq_stats {
u64 wait_time; // 等待时间
u64 run_time; // 运行时间
u64 idle_time; // 空闲时间
u64 iowait_time; // I/O等待时间
};
6.1.2 任务调度统计
c
struct sched_statistics {
u64 wait_start; // 等待开始时间
u64 sleep_start; // 睡眠开始时间
u64 block_start; // 阻塞开始时间
u64 sleep_max; // 最大睡眠时间
u64 block_max; // 最大阻塞时间
};
6.2 调试接口
6.2.1 proc文件系统
bash
# 查看CPU调度信息
cat /proc/sched_debug
# 查看运行队列信息
cat /proc/schedstat
# 查看CPU负载信息
cat /proc/loadavg
6.2.2 跟踪事件
c
// 调度相关跟踪点
TRACE_EVENT(sched_switch,
TP_PROTO(struct task_struct *prev, struct task_struct *next),
TP_ARGS(prev, next)
);
TRACE_EVENT(sched_wakeup,
TP_PROTO(struct task_struct *p),
TP_ARGS(p)
);
6.3 性能分析工具
6.3.1 systrace
bash
# 捕获调度事件
python systrace.py sched freq idle -o trace.html
6.3.2 perf
bash
# 分析调度事件
perf record -e sched:sched_switch -a
perf report
6.3.3 simpleperf
bash
# Android性能分析
simpleperf record -p <pid> -e cpu-clock
simpleperf report
7. 调用流程详解
7.1 任务创建流程
do_fork() → wake_up_new_task() → select_task_rq() → activate_task()
7.2 任务唤醒流程
try_to_wake_up() → select_task_rq() → ttwu_queue() → enqueue_task()
7.3 上下文切换流程
__schedule() → pick_next_task() → context_switch() → switch_to()
7.4 负载均衡流程
load_balance() → find_busiest_group() → detach_tasks() → attach_tasks()
8. 优化技巧与最佳实践
8.1 应用层优化
- 合理设置线程优先级
- 使用线程池避免频繁创建销毁
- 避免主线程阻塞操作
- 使用JobScheduler处理后台任务
8.2 系统层优化
- 调整调度参数
- 优化CPU拓扑配置
- 配置合适的调度策略
- 启用能量感知调度
8.3 调试技巧
- 使用systrace分析调度问题
- 监控CPU利用率分布
- 检查负载均衡效果
- 分析上下文切换频率
9. 总结
Android CPU调度优化是一个复杂的系统工程,涉及硬件、内核、框架多个层次。理解其原理和实现细节,有助于开发高性能、低功耗的应用和系统。
关键要点:
- 层次化设计: 从硬件到应用的多层优化
- 算法优化: CFS、PELT、EAS等核心算法
- 能量感知: 平衡性能与功耗
- 实时监控: 完善的调试和分析工具
- 持续优化: 根据使用场景调整策略
通过深入理解这些原理和实现,可以更好地优化Android设备的性能和用户体验。