加入收藏 | 设为首页 | 会员中心 | 我要投稿 财气旺网 - 财气网 (https://www.caiqiwang.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Windows > 正文

Linux内核的进程负载均衡机制

发布时间:2019-04-12 09:24:51 所属栏目:Windows 来源:金庆辉
导读:概述 在多核系统中,为了更好的利用多CPU并行能力,进程调度器可以将进程负载尽可能的平均到各个CPU上。再具体实现中,如何选择将进程迁移到的目标CPU,除了考虑各个CPU的负载平衡,还需要将Cache利用纳入权衡因素。同时,对于进程A唤醒进程B这个模型,还
副标题[/!--empirenews.page--]

概述

在多核系统中,为了更好的利用多CPU并行能力,进程调度器可以将进程负载尽可能的平均到各个CPU上。再具体实现中,如何选择将进程迁移到的目标CPU,除了考虑各个CPU的负载平衡,还需要将Cache利用纳入权衡因素。同时,对于进程A唤醒进程B这个模型,还做了特殊的处理。本文分析以Centos kernel 3.10.0-975源码为蓝本。

SMP负载均衡模型

问题

如果只是将CPU负载平均的分布在各个CPU上,那么就无所谓需要调度域。但是由于Cache以及内存Numa的存在,使得进程最好能迁移到与之前运行所在CPU更'近'的CPU上。

以我们常用的Intel X86为例。Cache基本视图如下图:

Linux内核的进程负载均衡机制

Linux内核的进程负载均衡机制

从Cache和内存访问的视角,如果进程负载均衡需要把进程A迁移到另一个CPU上,

  • 如果目标CPU和进程A之前所在CPU正好是同一个物理CPU同一个核心上(超线程),那么Cache利用率最好,毕竟L1,L2和L3中还是'热'的。
  • 如果目标CPU和进程A之前所在CPU正好是同一个物理CPU但不同核心上(多核),那么Cache利用率次之,L3中还有'热'数据。
  • 如果目标CPU和进程A之前所在CPU正好是同一个NUMA但是不同物理CPU上(多NUMA结构),虽然Cache已经是'冷'了,但至少内存访问还是在本NUMA中。
  • 如果目标CPU和进程A之前所在CPU在不同NUMA中,不但Cache是'冷'的,跨NUMA内存还有惩罚,此时内存访问速度最差。

SMP组织

为了更好地利用Cache,内核将CPU(如果开启了超线程,那么以逻辑CPU为单位,否则以物理CPU核心为单位)组织成了调度域。

逻辑视角

假设某机器为2路4核8核心CPU,它的CPU调度域逻辑上如下图:

 

2路NUMA最为简单,如果是4路NUMA,那么这个视图在NUMA层级将会复杂很多,因为跨NUMA访问根据访问距离导致访问延时还不相同,这部分最后讨论。

分层视角

所有CPU一共分为三个层次:SMT,MC,NUMA,每层都包含了所有CPU,但是划分粒度不同。根据Cache和内存的相关性划分调度域,调度域内的CPU又划分一次调度组。越往下层调度域越小,越往上层调度域越大。进程负载均衡会尽可能的在底层调度域内部解决,这样Cache利用率最优。

从分层的视角分析,下图是调度域实际组织方式,每层都有per-cpu数组保存每个CPU对应的调度域和调度组,它们是在初始化时已经提前分配的内存。值得注意的是

  • 每个CPU对应的调度域数据结构都包含了有效的内容,比如说SMT层中,CPU0和CPU1对应的不同调度域数据结构,内容是一模一样的。
  • 每个CPU对应的调度组数据结构不一定包含了有效内容,比如说MC层中,CPU0和CPU1指向不同的struct sched_domain,但是sched_domain->groups指向的调度组确是同样的数据结构,这些调度组组成了环。
 

单CPU视角

从单个CPU的视角分析,下图是调度域实际组织方式。

Linux内核的进程负载均衡机制

Linux内核的进程负载均衡机制

每个CPU的进程运行队列有一个成员指向其所在调度域。从最低层到最高层。

我们可以在/proc/sys/kernel/sched_domain/cpuX/ 中看到CPU实际使用的调度域个数以及每个调度域的名字和配置参数。

负载均衡时机

  • 周期性调用进程调度程序scheduler_tick()->trigger_load_balance()中,通过软中断触发负载均衡。
  • 某个CPU上无可运行进程,__schedule()准备调度idle进程前,会尝试从其它CPU上pull一批进程过来。

周期性负载均衡

CPU对应的运行队列数据结构中记录了下一次周期性负载均衡的时间,当超过这个时间点后,将触发SCHED_SOFTIRQ软中断来进行负载均衡。

  1. void trigger_load_balance(struct rq *rq, int cpu) 
  2.         /* Don't need to rebalance while attached to NULL domain */ 
  3.         if (time_after_eq(jiffies, rq->next_balance) && 
  4.             likely(!on_null_domain(cpu))) 
  5.                 raise_softirq(SCHED_SOFTIRQ); 
  6. #ifdef CONFIG_NO_HZ_COMMON 
  7.         if (nohz_kick_needed(rq) && likely(!on_null_domain(cpu))) 
  8.                 nohz_balancer_kick(cpu); 
  9. #endif 

(编辑:财气旺网 - 财气网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!