我们来详细讲解一下分析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_cpus
和 nr_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_group
和 spin_lock
。然后为当前CPU创建一个调度域,domain_level
值为 0。
示例二:双核系统初始化
假设我们有一台双核系统。在调度器初始化时,smp_num_cpus
和 nr_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_group
和 spin_lock
。然后为当前CPU创建一个调度域,domain_level
值为 0。
对于另一个CPU,将重新创建 spin_lock
并为其创建一个新的调度域,domain_level
值为 0。
这样,我们就通过以上两个示例对进程调度器的初始化过程进行了说明,并对初始化函数和重要函数进行了分析。当然,对于这个过程的详细分析还需要更多的工作和实践经验。