一.函数实现
cs
unsigned long cpu_util_cfs_boost(int cpu)
8267 {
8268 unsigned long util = INT_MAX;
8269
8270 trace_android_rvh_cpu_util_cfs_boost(cpu, &util);
8271 if (util != INT_MAX)
8272 return util;
8273
8274 return cpu_util(cpu, NULL, -1, 1);
8275 }
static unsigned long
8197 cpu_util(int cpu, struct task_struct *p, int dst_cpu, int boost)
8198 {
8199 struct cfs_rq *cfs_rq = &cpu_rq(cpu)->cfs;
8200 unsigned long util = READ_ONCE(cfs_rq->avg.util_avg);
8201 unsigned long runnable;
8202
8203 if (boost) {
8204 runnable = READ_ONCE(cfs_rq->avg.runnable_avg);
8205 util = max(util, runnable);
8206 }
8207
8208 /*
8209 * If @dst_cpu is -1 or @p migrates from @cpu to @dst_cpu remove its
8210 * contribution. If @p migrates from another CPU to @cpu add its
8211 * contribution. In all the other cases @cpu is not impacted by the
8212 * migration so its util_avg is already correct.
8213 */
8214 if (p && task_cpu(p) == cpu && dst_cpu != cpu)
8215 lsub_positive(&util, task_util(p));
8216 else if (p && task_cpu(p) != cpu && dst_cpu == cpu)
8217 util += task_util(p);
8218
8219 if (sched_feat(UTIL_EST)) {
8220 unsigned long util_est;
8221
8222 util_est = READ_ONCE(cfs_rq->avg.util_est);
8223
8224 /*
8225 * During wake-up @p isn't enqueued yet and doesn't contribute
8226 * to any cpu_rq(cpu)->cfs.avg.util_est.
8227 * If @dst_cpu == @cpu add it to "simulate" cpu_util after @p
8228 * has been enqueued.
8229 *
8230 * During exec (@dst_cpu = -1) @p is enqueued and does
8231 * contribute to cpu_rq(cpu)->cfs.util_est.
8232 * Remove it to "simulate" cpu_util without @p's contribution.
8233 *
8234 * Despite the task_on_rq_queued(@p) check there is still a
8235 * small window for a possible race when an exec
8236 * select_task_rq_fair() races with LB's detach_task().
8237 *
8238 * detach_task()
8239 * deactivate_task()
8240 * p->on_rq = TASK_ON_RQ_MIGRATING;
8241 * -------------------------------- A
8242 * dequeue_task() \
8243 * dequeue_task_fair() + Race Time
8244 * util_est_dequeue() /
8245 * -------------------------------- B
8246 *
8247 * The additional check "current == p" is required to further
8248 * reduce the race window.
8249 */
8250 if (dst_cpu == cpu)
8251 util_est += _task_util_est(p);
8252 else if (p && unlikely(task_on_rq_queued(p) || current == p))
8253 lsub_positive(&util_est, _task_util_est(p));
8254
8255 util = max(util, util_est);
8256 }
8257
8258 return min(util, arch_scale_cpu_capacity(cpu));
8259 }
从 cpu_util(cpu, NULL, -1, 1) 来看 会执行 util = max(util_avg, runnable_avg);
if (boost) { 8204 runnable = READ_ONCE(cfs_rq->avg.runnable_avg); 8205 util = max(util, runnable); 8206 }
🔍 一、先明确两个指标的定义(来自 PELT)
✅ util_avg(平均利用率)
- 定义 :任务 实际在 CPU 上执行 的时间比例。
- 公式 :
running_time / total_time × 1024 - 范围:0 ~ 1024
- 特点 :
- 只统计 真正占用 CPU 周期 的时间;
- 如果任务在等待 I/O、锁、sleep,这段时间 不计入。
📌 代表 CPU 的"实际使用率"。
✅ runnable_avg(平均可运行率)
- 定义 :任务处于 runnable 状态 的时间比例。
- 包括:
- 正在 CPU 上运行(running);
- 在运行队列中等待调度(waiting for CPU)。
- 包括:
- 公式 :
runnable_time / total_time × 1024 - 范围:0 ~ 1024
- 特点 :
- 只要任务 没阻塞(没 sleep、没 wait I/O),就算 runnable;
- 即使 CPU 忙,任务在排队,也算 runnable。
📌 代表 任务对 CPU 的"需求压力"。
🆚 二、关键区别:util_avg ≤ runnable_avg 恒成立
因为:
- running ⊆ runnable
- 所以:
实际运行时间 ≤ 可运行时间 - 因此:
util_avg ≤ runnable_avg
只有当 CPU 无竞争(单任务独占 CPU) 时,两者才相等。
📊 三、典型场景分析
场景 1:单任务独占 CPU(无竞争)
- 任务一直运行,无等待。
util_avg = 1024runnable_avg = 1024max = 1024→ 正确:CPU 满载。
场景 2:两个 CPU-bound 任务在 1 个 CPU 上
- 每个任务运行 50% 时间。
util_avg = 512(每个任务平均)runnable_avg = 1024(始终有任务可运行)max = 1024→ 正确反映 CPU 已满载!
❌ 如果只用
util_avg = 512,调度器会误以为 CPU 还有 50% 空闲,可能错误地迁移更多任务过来,导致更严重的排队。
场景 3:任务频繁 sleep(I/O-bound)
- 任务运行 10ms,sleep 90ms。
util_avg ≈ 102runnable_avg ≈ 102(因为 sleep 期间不可运行)max = 102→ 正确:CPU 负载低。
场景 4:任务在等锁(runnable 但不 running)
- 任务 A 持有锁,任务 B 在等锁(但处于 runnable 状态)。
- B 的
runnable_avg高,但util_avg = 0。 - 若 A+B 都在同一个 CPU:
cfs_rq->avg.runnable_avg会很高;max(util_avg, runnable_avg)→ 高值;- 调度器知道:这个 CPU 虽然实际运行时间不高,但任务在"忙等",可能需要优化或迁移。
🎯 四、为什么调度器需要这个"max"值?
1. 负载均衡(Load Balancing)
- 目标:让每个 CPU 的 负载压力均衡,而不是"实际运行时间"均衡。
- 如果只看
util_avg,会把任务迁移到"看似空闲实则排队"的 CPU,恶化性能。
2. 能效调度(EAS)
- 决定任务放大小核:需要知道 真实需求 ,而不是被调度延迟压低的
util_avg。
3. CPU 调频(schedutil)
- 如果
runnable_avg = 1024,说明任务需要 100% 算力,应拉高频率; - 即使当前
util_avg = 600(因频率低导致运行慢),也要升频。
✅
max(util_avg, runnable_avg)是对"CPU contention(争抢)"的直接检测。
📌 五、总结
max(util_avg, runnable_avg)返回的是:
"该 CPU 上 CFS 任务所施加的 最大调度压力 ",
它能区分 CPU 真空闲 和 CPU 忙但任务在排队 ,
是现代调度器实现 稳定、高效、低抖动 的基石之一。