分析Linux内核调度器源码之初始化

  • Post category:Linux

我们来详细讲解一下分析Linux内核调度器源码之初始化的完整攻略。

一、准备工作

在分析Linux内核调度器源码之前,需要对一些基础知识有所了解:

  • C语言编程语言基础
  • Linux 操作系统基础知识
  • 熟悉操作系统中进程的调度机制

同时需要有一定的代码阅读能力,对代码的调试也有一定的经验。

二、查看调度器初始化代码

在Linux内核源码中,进程调度器的代码位于 kernel/sched/ 目录下。我们需要查看它的初始化函数,以了解它的初始化流程。在 kernel/sched/core.c 文件中,找到下面的代码:

void __init sched_init(void)
{
    int i;

    for_each_possible_cpu(i)
        cpu_attach_domain(i);
}

这是进程调度器的初始化函数。它遍历了每一个可能的CPU,并为每一个CPU创建一个调度域。

接下来,我们需要了解 cpu_attach_domain() 函数的实现。

三、分析 cpu_attach_domain() 函数

cpu_attach_domain() 函数的定义在 kernel/sched/topology.c 文件中。它用于为每个CPU配置调度域。下面是函数的实现:

void __init cpu_attach_domain(int cpu)
{
    struct sched_domain *sd;
    int i;

    for_each_domain(cpu, sd) {
        for (i = 0; i < sd->span_depth; i++) {
            if (sd->span[i])
                continue;

            /* ... */
        }

        /* ... */
    }

    /* ... */
}

在函数内部有一个 for_each_domain() 循环。该循环用于为每个调度域设置 cpu 的 cpu power 和 cpu’s sched group 的值。它通过之前解析的 for_each_possible_cpu() 展现出极高的灵活性。

当调度域的 span 数组中的某个值为 NULL 时,它就会创建一个 sched_group 结构并将其赋值给 span 数组。在这个过程中,调度器为每个CPU分配了一个 sched_group,以便在每个CPU上进行进程调度。

例如,在一个双核系统中,每个CPU都有它自己的 sched_group 结构和 spin_lock。当这两个CPU中的一个CPU公正地释放锁并调用进程调度器时,另一个CPU也能够获得锁并将相应的进程调度切换到当前CPU。

四、示例

这里给出两个示例,以帮助理解上面所述的调度器初始化函数:

示例一:单核系统初始化

假设我们有一台单核系统。在调度器初始化时,smp_num_cpusnr_cpu_ids 均为 1,因此 for_each_possible_cpu() 循环将只对单个CPU执行:

void __init sched_init(void)
{
    int i;

    for_each_possible_cpu(i)
        cpu_attach_domain(i);
}

cpu_attach_domain() 函数将为当前CPU创建 sched_groupspin_lock。然后为当前CPU创建一个调度域,domain_level 值为 0。

示例二:双核系统初始化

假设我们有一台双核系统。在调度器初始化时,smp_num_cpusnr_cpu_ids 均为 2,因此 for_each_possible_cpu() 循环将对两个CPU执行:

void __init sched_init(void)
{
    int i;

    for_each_possible_cpu(i)
        cpu_attach_domain(i);
}

cpu_attach_domain() 函数将为当前CPU创建 sched_groupspin_lock。然后为当前CPU创建一个调度域,domain_level 值为 0。

对于另一个CPU,将重新创建 spin_lock 并为其创建一个新的调度域,domain_level 值为 0。

这样,我们就通过以上两个示例对进程调度器的初始化过程进行了说明,并对初始化函数和重要函数进行了分析。当然,对于这个过程的详细分析还需要更多的工作和实践经验。