Linux调度域与调度组

本文详述了Linux内核中调度域和调度组的概念及其数据结构,包括调度域的层级结构、调度域对象的构建、调度组的定义与能力初始化,以及系统调度域拓扑的构建过程,旨在帮助读者深入理解调度域在负载均衡中的作用。
摘要由CSDN通过智能技术生成

引入调度域的讨论可以参考这篇文章。这篇笔记重点分析了内核调度域相关的数据结构以及内核用于构建调度域的代码实现,以此来加深对调度域的理解。调度域是调度器进行负载均衡的基础。

调度域拓扑层级

整个系统的调度域组成一个层级结构,内核设计了struct sched_domain_topology_level来描述一层调度域拓扑。

typedef const struct cpumask *(*sched_domain_mask_f)(int cpu);
typedef int (*sched_domain_flags_f)(void);

struct sched_domain_topology_level {
    sched_domain_mask_f mask;
    sched_domain_flags_f sd_flags;

    int    flags;
    int    numa_level;
    struct sd_data      data;
#ifdef CONFIG_SCHED_DEBUG
    char *name;
#endif
};
  • mask:该回调用于指定该层级的调度域的CPU掩码。
  • sd_flags:该回调用于获取该层级的调度域标记。
  • data:该层级的调度域对象,见下面单独分析。
  • name:层级名字,如MC、DIE,这些名字会在用户态的/proc/sys/kernel/sched_domain目录中体现。

系统的调度域拓扑用sched_domain_topology_level[]数组表示,保存在全局变量sched_domain_topology中,数组的每一个元素代表一个层级。default_topology是系统定义的默认调度域拓扑层级数组。各体系结构可以定义自己的调度域拓扑层级数组,然后通过set_sched_topology()函数替换该默认值。

// 不考虑超线程,默认的由MC(多核)和DIE(socket)两个层级组成,手机产品基本上只有这两个层级
static struct sched_domain_topology_level default_topology[] = {
#ifdef CONFIG_SCHED_SMT
    { cpu_smt_mask, cpu_smt_flags, SD_INIT_NAME(SMT) }, // 支持超线程时开启
#endif
#ifdef CONFIG_SCHED_MC
    { cpu_coregroup_mask, cpu_core_flags, SD_INIT_NAME(MC) },
#endif
    { cpu_cpu_mask, SD_INIT_NAME(DIE) },
    { NULL, },
};

struct sched_domain_topology_level *sched_domain_topology = default_topology;

// MC层级包含了属于同一个cluster的所有CPU
const struct cpumask *cpu_coregroup_mask(int cpu)
{
    return &cpu_topology[cpu].core_sibling;
}

// DIE层级包含了系统所有的CPU(不考虑NUMA)
static inline const struct cpumask *cpu_cpu_mask(int cpu)
{
    return cpumask_of_node(cpu_to_node(cpu));
}

sd_data

该结构用于辅助定义调度域拓扑层级,包含了一个拓扑层级所有的调度域对象,这些对象每个CPU一份。由于Per-CPU变量也是动态分配的,所以类型是二级指针。此外,由于整个调度域拓扑层级一旦建立就基本不会再发生变化,每个CPU一份可以提高访问效率。

struct sd_data {
    struct sched_domain **__percpu sd;
    struct sched_group **__percpu sg;
    struct sched_group_capacity **__percpu sgc;
};

构建系统调度域拓扑结构时,会使用__sdt_alloc()为sd_data分配内存,对应的释放函数为__sdt_free()

static int __sdt_alloc(const struct cpumask *cpu_map)
{
    struct sched_domain_topology_level *tl;
    int j;

    for_each_sd_topology(tl) { // 遍历所有的拓扑层级,为每一个tl->sd_data分配空间
        struct sd_data *sdd = &tl->data;

        // 分配调度域指针对象,这些指针每个CPU一份
        sdd->sd = alloc_percpu(struct sched_domain *);
        sdd->sg = alloc_percpu(struct sched_group *);
        sdd->sgc = alloc_percpu(struct sched_group_capacity *);

        // 为每个CPU分配这三个调度域对象
        for_each_cpu(j, cpu_map) {
            struct sched_domain *sd;
            struct sched_group *sg;
            struct sched_group_capacity *sgc;

            sd = kzalloc_node(sizeof(struct sched_domain) + cpumask_size(),
                GFP_KERNEL, cpu_to_node(j));
            *per_cpu_ptr(sdd->sd, j) = sd;

            sg = kzalloc_node(sizeof(struct sched_group) + cpumask_size(),
                GFP_KERNEL, cpu_to_node(j));
            sg->next = sg;
            *per_cpu_ptr(sdd->sg, j) = sg;

            sgc = kzalloc_node(sizeof(struct sched_group_capacity) + cpumask_size(),
                GFP_KERNEL, cpu_to_node(j));
            *per_cpu_ptr(sdd->sgc, j) = sgc;
        }
    }
    return 0;
}

调度域对象

有3个调度域对象:

  1. struct sched_domain:调度域代表可以共享属性和调度参数的一组CPU。每个CPU在每一个调度域拓扑层级中都有一个shced_domain对象。
  2. struct sched_group:调度域可以由一个或多个调度组构成,每个调度组也代表可以共享属性和调度参数的一组CPU,属于同一个调度域的调度组的集合组成了调度域代表的CPU。调度域进行负载均衡的目的就是要保证其内部各个调度组之间的负载均衡。
  3. struct sched_capacity:包含了调度组的ca
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值