了解一下kernel6.12中cpu_util_cfs_boost函数的逻辑

一.函数实现

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 = 1024
  • runnable_avg = 1024
  • max = 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 ≈ 102
  • runnable_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 忙但任务在排队
是现代调度器实现 稳定、高效、低抖动 的基石之一。

相关推荐
Violet_YSWY3 小时前
final是干嘛的
java·开发语言
一只小透明啊啊啊啊4 小时前
Java的中间件
java·开发语言·中间件
学编程就要猛4 小时前
数据结构初阶:Java中的ArrayList
java·开发语言·数据结构
JH30734 小时前
10分钟理解泛型的通配符(extends, super, ?)
java·开发语言·windows
wifi chicken4 小时前
Linux Wlan 无线协议栈开发-传输层详解
linux·网络协议
Tony Bai4 小时前
【Go模块构建与依赖管理】01 前世今生:从 GOPATH 的“混乱”到 Go Modules 的“秩序”
开发语言·后端·golang
XH-hui5 小时前
【打靶日记】THL 之 Facultad
linux·网络安全·1024程序员节·thehackerlabs
熙xi.5 小时前
DHT11温湿度传感器Linux驱动开发完整流程
linux·运维·驱动开发
缺点内向5 小时前
Java 使用 Spire.XLS 库合并 Excel 文件实践
java·开发语言·excel