深入理解 Linux 进程管理之 CFS 负载均衡
什么是负载均衡?
为了 CPU 之间减少“干扰”,每个 CPU 上都有一个任务队列。运行的过程种可能会出现有的 CPU“忙的一笔”,有的 CPU“闲的蛋疼”,于是便需要负载均衡。
将 task 从负载较重的 CPU 上转移到负载相对较轻的 CPU 上执行,这个过程就是负载均衡的过程。
在了解负载均衡前有必要了解 soc 上对 CPU 的拓扑关系。
我们知道一个多核心的 soc 片上系统,内部结构是很复杂的,内核采用 CPU 拓扑结构来描述一个 SOC 的架构。内核使用调度域来描述 CPU 之间的层次关系,对于低级别的调度域来说,CPU 之间的负载均衡处理开销比较小,而对于越高级别的调度域,其负载均衡的开销就越大。
比如一个 4 核心的 SOC,两个核心是一个 cluster,共享 L2 cache,那么每个 cluster 可以认为是一个 MC 调度域,每个 MC 调度域中有两个调度组,每个调度组中只有一个 CPU。而整个 SOC 可以认为是高一级别的 DIE 调度域,其中有两个调度组,cluster0 属于一个调度组,cluster1 属于另一个调度组。跨 cluster 的负载均衡是需要清除 L2 cache 的,开销是很大的,因此 SOC 级别的 DIE 调度域进行负载均衡的开销会更大一些。
CPU 对应的调度域和调度组可通过在设备模型文件 /proc/sys/kernel/sched_domain 里查看。
调度域 sched_domain 主要的成员如下:
调度组 sched_group 主要的成员如下:
CPU 拓扑示例
为了减少锁的竞争,每一个 cpu 都有自己的 MC domain、DIE domain(sched domain 是分成两个 level,base domain 称为 MC domain(multi core domain),顶层的 domain 称为 DIE domain)以及 sched group,并且形成了 sched domain 之间的层级结构,sched group 的环形链表结构。可以通过/sys/devices/system/cpu/cpuX/topology 查看 cpu topology 信息。
在上面的结构中,sched domain 是分成两个 level,base domain 称为 MC domain,顶层的 domain 称为 DIE domain。顶层的 DIE domain 覆盖了系统中所有的 CPU,小核 cluster 的 MC domain 包括所有小核 cluster 中的 cpu,大核 cluster 的 MC domain 包括所有大核 cluster 中的 cpu。
通过 DTS 和 CPU topo 子系统,可以构建 sched domain 层级结构,用于具体的均衡算法。流程是:kernel_init() -> kernel_init_freeable() -> smp_prepare_cpus() -> init_cpu_topology() -> parse_dt_topology()
【文章福利】另外小编还整理了一些 C++后端开发面试题,教学视频,后端学习路线图免费分享,需要的可以自行添加:学习交流群点击加入~ 群文件共享
小编强力推荐 C++后端开发免费学习地址:C/C++Linux服务器开发高级架构师/C++后台开发架构师
负载均衡的软件架构
图中可以看出左边主要分为 CPU 负载跟踪和 task 负载跟踪。
CPU 负载跟踪:考虑每一个 CPU 的负载。汇聚 cluster 上所有负载,方便计算 cluster 之间负载的不均衡状况。
task 负载跟踪:判断该任务是否适合当前 CPU 算力。如果判定需要均衡,那么需要在 CPU 之间迁移多少的任务才能达到平衡。
右边是通过 DTS 和 CPU topo 子系统,构建的 sched domain 层级结构。流程是:kernel_init() -> kernel_init_freeable() -> smp_prepare_cpus() -> init_cpu_topology() -> parse_dt_topology()
有了左右两边的基础设施,那么什么时候触发负载均衡呢?这主要和调度事件相关,当发生任务唤醒、任务创建、tick 到来等调度事件的时候,就可以检查当前系统的不均衡情况,并酌情进行任务迁移,以便让系统负载处于平衡状态。
何时做负载均衡?
CFS 任务的负载均衡器有两种。一种是为繁忙 CPU 们准备的 periodic balancer,用于 CFS 任务在 busy cpu 上的均衡;一种是为 idle cpu 们准备的 idle balancer,用于把繁忙 CPU 上的任务均衡到 idle cpu 上来。
周期性负载均衡(periodic load balance 或者 tick load balance)是指在 tick 中,周期性的检测系统的负载均衡状况,找到系统中负载最重的 domain、group 和 CPU,将其上的 runnable 任务拉到本 CPU 以便让系统的负载处于均衡的状态。
nohz load balance 是指其他的 cpu 已经进入 idle,本 CPU 任务太重,需要通过 IPI 将其他 idle 的 CPUs 唤醒来进行负载均衡。nohz idle load balance 也是通过 busy cpu 上 tick 驱动的,如果需要 kick idle load balancer,那么就会通过 GIC 发送一个 ipi 中断给选中的 idle cpu,让它代表系统所有的 idle cpu 们进行负载均衡。
new idle load balance 比较好理解,就是在 CPU 上没有任务执行,马上要进入 idle 状态的时候,看看其他 CPU 是否需要帮忙,来从 busy cpu 上拉任务,让整个系统的负载处于均衡状态。
负载均衡的基本过程
当一个 CPU 上进行负载均衡的时候,总是从 base domain 开始,检查其所属 sched group 之间的负载均衡情况,如果有不均衡情况,那么会在该 cpu 所属 cluster 之间进行迁移,以便维护 cluster 内各个 cpu core 的任务负载均衡。
load_balance 是处理负载均衡的核心函数,它的处理单元是一个调度域,也就是 sched domain,其中会包含对调度组的处理。
在该 domain 中找到最忙的 sched group
在最忙的 group 中挑选最忙的 CPU runqueue,该 CPU 就成为任务迁移的 src
从该队列中选择要迁移的任务(判断的依据主要是 task load 的大小,优先选择 load 重的任务)
向着作为 dst 的 CPU runqueue 迁移
参考资料
推荐一个零声教育 C/C++后台开发的免费公开课程,个人觉得老师讲得不错,分享给大家:C/C++后台开发高级架构师,内容包括Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习
评论