Linux 6.10 | CPU 调度

前言

本文基于MTK 平台、kernel 6.10

画了几个框图以及选了两个案例分析,不会拘泥于代码细节,先有个大致的了解

唤醒场景下的选核

选核涉及多种不同的场景,这里以fair task 唤醒选核为例进行说明。 其实不论是唤醒fork task还是唤醒阻塞task,都会走到select_task_rq。

MTK 平台上hook 了#1 及#2,分别用于task_tubro 以及EAS 场景下的选核

注:task_turbo 主要是针对启动场景

Task turbo 选核

EAS 选核

这里只画了主干路径,一些不常见的路径或feature 强相关的没有体现

注:EAS 只有在未处于overutilized 状态下才会启用,至于overutilized 如何判定,mtk 有客制化。

下面分Part1 & Part2 介绍。

Part1:Root domain

对于CPU 而言,overload 和overutilized 这两个是不同的概念

  • CPU overload:runqueue 有 ≥2 任务,或 1 个 misfit task。
  • CPU overutilized:utility > capacity(预留 20% 算力)。
  • Root Domain overload:至少一个 CPU overload。
  • Root Domain overutilized:至少一个 CPU overutilized。

注:原生上是针对Root domain 而言的

Part2:判定overutilized

Mtk hook 后针对perf domain 而言,总利用率超过总容量的 80%,则认定CPU 处于 overutilized 状态,这里有别于原生。

Ftrace eg:

Part3:Sync 唤醒且Curr cpu running =1

这个条件比较苛刻,了解即可。

要求:wake_flags 包含WF_SYNC,且这个进程没有正在退出,及需fit 这个cpu,且cur cpu 队列上只有一个任务。

#define WF_SYNC 0x10 /* Waker goes to sleep after wakeup */

select reason: #define LB_SYNC (0x02)

Part4:选择候选CPU 集合

这块策略是mtk 客制化的,同样的我们只梳理主干路径,跳过一些比如判断是否处于中断、CPU 是否被限流、是否是latency_sensitive 等路径。

1.先遍历cluster,并遍历cluster 中每个CPU

2.记录CPU 的利用率(cpu_util)和空闲容量(spare_cap)。

3.如果task 是VIP ,则优先选择 VIP 任务数量最少的 CPU。如果当前CPU 的 VIP 任务数量比之前的最小值更小,则更新候选 CPU 集合。

c++ 复制代码
/*don't choice CPU only because it can calc energy, choice min_num_vip CPU */
    if ((prev_min_num_vip != UINT_MAX) && (prev_min_num_vip != min_num_vip))
    		cpumask_clear(candidates);

比如此时候选CPU 是CPU 5以及CPU 6,它们上面的vip 数目分别是2和3,此时遍历到CPU 7时,它的上面vip 数目只有1,此时清空CPU 5以及CPU 6,将CPU 7加入候选。

注:挑选剩余计算能力最大的CPU 并不意味着功耗高,相反很多时候会让CPU 跑到更低的freq 上,反而节省功耗。

  1. 比较剩余最大计算能力的CPU

如果p 是VIP,则依旧保持最少vip 数目的CPU优先,如果数目和min_vip_num 一致的话,优先选大的。如果p 是非VIP 的话,则优先选择剩余计算能力大的的CPU。

Part5:候选CPU 集 中选最节能的CPU

从候选 CPU 集合(candidates)中选择一个<能量最优>的 CPU 来运行任务 p。 通过计算每个候选 CPU 的能量消耗差异(cur_delta),选择能量消耗最小的 CPU 作为最佳选择。

EAS覆盖了CFS的任务唤醒平衡代码。在唤醒平衡时,它使用平台的EM和PELT信号来选择节能的目标CPU。当EAS被启用时,select_task_rq_fair()调用find_energy_efficient_cpu() 来做任务放置决定。这个函数寻找在每个性能域中寻找具有最高剩余算力(CPU算力 - CPU利用率)的CPU,因为它能让我们保持最低的频率。然后,该函数检查将任务放在新CPU相较依然放在之前活动的prev_cpu是否可以节省能量.

如果唤醒的任务被迁移,find_energy_efficient_cpu()使用compute_energy()来估算 系统将消耗多少能量。compute_energy()检查各CPU当前的利用率情况,并尝试调整来 "模拟"任务迁移。EM框架提供了API em_pd_energy()计算每个性能域在给定的利用率条件 下的预期能量消耗。

docs.kernel.org/scheduler/s...

Trace 分析 & Energy 最优

好,现在有了前面的论述,我们通过Trace 看一个选择最优Energy 的例子

以System_Server 的InputReader 如下这个片段为例

之所以最终选择了CPU 6

可以参考上面流程图中的关键算法code,然后带入Ftrace 中的数值计算下

js 复制代码
cur_delta = max(cur_delta, base_energy) - base_energy; 
if (cur_delta < best_delta) { 
    best_delta = cur_delta; 
    best_energy_cpu = cpu; 
}

CPU 3:cur_delta = max(723084, 692956) - 692956 = 723084 - 692956 = 30128

CPU 6:cur_delta = max(1814629, 1785263) - 1785263 = 1814629 - 1785263 = 29366

cur_delta 可以简单理解为表示将任务迁移到某个 CPU 后,带来的Energy 增量

EAS 这部分牵扯的内容比较多也比较复杂,后面计划另起一文详细梳理。

Load balance

这里以常见的tick balance 举例,同样的还是mtk 平台上

其它几种balance,本质都差不多,最终都会触发load_balance 这一函数

案例分析

为便于理解,下面举两个简单的例子

Migration 导致的delay

中断关闭导致的延迟

正常片段应该类似如下

在这77ms 时间内,Sf 一直尝试选核到CPU 7上均没有成功,直到70 多ms 后才选了CPU 6.

这段时间内CPU 7上没有收到任何中断,说明中断被关闭,此时CPU 7上运行的线程正在等底层回传buffer

结语:

Android 上之所以采用CFS,单纯因为Android 诞生的时候Linux 已经采用了CFS,慢慢的Google 在上层顺势引入了Cgroup 分组控制等。

但是对于Android 这种强交互系统,某些场景下还不够,所以人们就自然的想到了哪些线程要优先跑,由自己来决定,于是在调度关键路径上的一系列hook 进行客制化,也就是各家宣称的"VIP"、"MVP" 等调度策略,这些的策略的堆叠使得原本已经复杂臃肿的CFS 架构更加复杂,使用不当各种副作用也会接踵而至。

简而言之,资源是一定的,任何策略注定都是顾此失彼,就看这个"失彼" 对用户的影响是否可以做到无感。

相关推荐
千航@abc5 分钟前
kvm虚拟机的基本使用
linux·运维·centos·虚拟化·kvm·virsh
敲上瘾8 分钟前
Linux信号的诞生与归宿:内核如何管理信号的生成、阻塞和递达?
linux·运维·服务器·c++·算法·信息与通信·信号处理
这个男人叫小帅25 分钟前
<项目> 主从Reactor模型的高并发服务器
linux·服务器·网络协议
AlfredZhao32 分钟前
Anaconda指定目录安装终极指南+避坑大全
linux·python·conda
luoqice1 小时前
在 ARM 嵌入式 Linux 下使用 C/C++ 实现 MQTT
linux·服务器·嵌入式硬件·iot
世事如云有卷舒1 小时前
Linux驱动学习笔记(六)
linux·笔记·学习
李小白杂货铺2 小时前
终端的命令行发送邮件的方式和监视脚本
linux·macos·终端发送邮件·命令后发送邮件·监视脚本·监视网络接口变化·监视网络接口返回
xiaguangbo2 小时前
Verilog-HDL/SystemVerilog/Bluespec SystemVerilog vscode 配置
linux·fpga
这个男人叫小帅2 小时前
<项目> 高并发服务器的HTTP协议支持
linux·服务器·网络·http
云水木石3 小时前
Linux 系统运行 Android 应用的几种方案
android·linux·运维·服务器