Linux核心概念详解
  • 0. Linux核心概念详解
  • 1. 调试环境
  • 2. Linux 调度器
    • 2.1 任务
    • 2.2 核心概念
      • 2.2.1 核心概念 - 调度实体
      • 2.2.2 核心概念 - 调度类
      • 2.2.3 核心概念 - 调度策略
      • 2.2.4 核心概念 - 运行队列
      • 2.2.5 核心概念 - 优先级
    • 2.3 演进历史
      • 2.3.1 O(n)调度器 - 调度逻辑
      • 2.3.2 O(n)调度器 - 时间分配
      • 2.3.3 O(n)调度器 - 调度时机
      • 2.3.4 O(1)调度器 - 简介
      • 2.3.5 O(1)调度器 - 调度逻辑
      • 2.3.6 O(1)调度器 - 时间分配
      • 2.3.7 RSDL
      • 2.3.8 CFS
    • 2.4 DL调度器
      • 2.4.1 DL调度器 - 调度算法
      • 2.4.2 DL调度器 -核心代码
    • 2.5 RT调度器
    • 2.6 CFS
      • 2.6.1 公平性
      • 2.6.2 调度逻辑
      • 2.6.2.1 调度逻辑 - 数据结构
      • 2.6.2.2 调度逻辑 - vruntime
      • 2.6.2.3 调度逻辑 - 调度周期
      • 2.6.2.4 调度逻辑 - 调度节拍
      • 2.6.2.5 调度逻辑 - 任务抢占
      • 2.6.2.6 调度逻辑 - 调度时机
      • 2.6.3 组调度
      • 2.6.3.1 组调度 - 数据结构
      • 2.6.3.2 组调度 - 调度逻辑
      • 2.6.3.3 组调度 - 时间分配
      • 2.6.3.4 组调度 - 任务组权重
      • 2.6.4 带宽控制
      • 2.6.4.1 带宽控制 - 数据结构
      • 2.6.4.2 带宽控制 - 带宽时间
      • 2.6.4.3 带宽控制 - 挂起与解挂
      • 2.6.4.3 带宽控制 - 定时器
    • 2.7 负载追踪
      • 2.7.1 负载追踪 - 简介
      • 2.7.2 负载追踪 - 数据结构
      • 2.7.3 负载追踪 - 计算负载
      • 2.7.4 负载追踪 - 更新负载
    • 2.8 负载均衡
      • 2.8.1 简介
      • 2.8.2 CPU的拓扑结构
      • 2.8.3 数据结构
      • 2.8.4 算法思路
      • 2.8.5 触发时机
      • 2.8.6 总结
  • 3. LINUX 内存管理
    • 3.1 寻址模式
      • 3.1.1 地址
      • 3.1.2 地址转换
      • 3.1.3 Linux的地址空间
    • 3.2 物理内存
      • 3.2.1 数据结构
      • 3.2.2 初始化
      • 3.2.3 物理内存模型
      • 3.2.4 Buddy System(伙伴系统)
      • 3.2.5 SLAB/SLUB/SLOB
Powered by GitBook
On this page

Was this helpful?

  1. 2. Linux 调度器
  2. 2.6 CFS

2.6.3.4 组调度 - 任务组权重

Previous2.6.3.3 组调度 - 时间分配Next2.6.4 带宽控制

Last updated 3 years ago

Was this helpful?

上一节在讲任务组中 se 的时间计算时,有一个点我们没有深究:即 cfsrq 的总权重如何计算?在介绍 struct task_group 时,有个字段叫 shares:

/* file: kernel/sched/sched.h */
struct task_group {
#ifdef CONFIG_FAIR_GROUP_SCHED
unsigned long shares;
#endif
};

正常来说,任务组的总体权重确定后,各个cfsrq按照各自的比例进行二次分配即可。如下图所示:

图中任务组的权重为1024,SE_R0中三个se的权重总计为3072,而SE_R1中两个se的权重也为3072,因此SE_R0与SE_R1平分任务组的权重,各占512. 由此可得计算公式:设tg表示整个任务组,grq表示任务组中分配到某个CPU上的cfsrq, ge表示指向该cfsrq的se, 则 se.weight = tg.shares * grq.weight/sum(grq.weight), 其中分母 sum(grq.weight) 表示任务组中所有 cfsrq 的权重之和。

计算权重的函数是 calc_group_shares():

/* file: kernsl/sched/fair.c */
static long calc_group_shares(struct cfs_rq *cfs_rq) {
long tg_weight, tg_shares, load, shares;
struct task_group *tg = cfs_rq->tg;

tg_shares = READ_ONCE(tg->shares);

load = max(scale_load_down(cfs_rq->load.weight), cfs_rq->avg.load_avg);

tg_weight = atomic_long_read(&tg->load_avg);

/* Ensure tg_weight >= load */
tg_weight -= cfs_rq->tg_load_avg_contrib;
tg_weight += load;

shares = (tg_shares * load);
if (tg_weight)
shares /= tg_weight;

return clamp_t(long, shares, MIN_SHARES, tg_shares);
}

函数中并没有使用前面我们总结出的公式来进行计算,因为同时访问所有CPU上的cfsrq是很昂贵的操作,需要对数据操作进行同步,因此这里使用的是任务组的平均负载(tg->loadavg)来替代权重。后续文章会详细讲解负载的计算方法,通常情况下平均负载与任务组的权重几乎相同,因此读者无需对该方法的具体实现感到困扰,内核的工程师们只是使用了一个更高效的方式来对前文中我们讨论的思路进行实现。

用户可以对任务组的权重进行设置,系统最终会调用如下方法来完成操作:

/* file: kernel/sched/fair.c */
int sched_group_set_shares(struct task_group *tg, unsigned long shares) {
int i;

/* 设置任务组的 shares */
tg->shares = shares;
/* 设置好任务组的总体 shares 之后,更新任务组在每个CPU上的se的权重信息 */
for_each_possible_cpu(i) {
struct rq *rq = cpu_rq(i);
struct sched_entity *se = tg->se[i];

/* 从当前se开始,沿着parent路径一路更新所有上层任务组的权重信息 */
for_each_sched_entity(se) {
    update_load_avg(cfs_rq_of(se), se, UPDATE_TG);
    update_cfs_group(se);
}
}
}

设置了任务组的权重之后,系统需要更新所有涉及到的任务组的权重信息,函数 update_cfs_group 会调用 calc_group_shares 计算 se 应该得到的配额,这里就不深入探讨了。