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

1.引言

在Linux基础知识中,了解Linux内核调度器是非常重要的一部分。内核调度器负责管理和分配CPU资源给不同的进程和线程,以实现任务的优先级和并发性。本文将深入探讨内核调度器的初始化,从而更好地理解其工作原理。

2.背景

在Linux操作系统中,内核调度器有多种实现方式,其中最常见的是完全公平调度(CFS)算法。CFS算法通过管理红黑树数据结构来维护进程队列,以实现公平分配CPU时间片。在初始化调度器之前,需要进行一些准备工作。

2.1 内核调度器初始化的位置

在Linux内核中,内核调度器的初始化位于kernel/sched/core.c文件中的init_sched()函数中。这个函数会被调用以初始化调度器在系统启动时的状态。

2.2 重要数据结构

在初始化期间,内核调度器使用了许多重要的数据结构,其中最重要的是struct sched_entitystruct cfs_rq。前者表示调度实体,用于表示一个进程或线程的调度状态。后者表示CFS运行队列,用于在进程之间分配CPU时间片。

3.内核调度器的初始化流程

下面将介绍内核调度器的初始化流程,以便更好地理解其工作方式。

3.1 初始化red-black树

在初始化之前,首先需要创建一个红黑树来管理进程队列。红黑树使用struct rb_root数据结构表示,并使用RB_ROOT宏来初始化根节点。这个红黑树将用于存储各个调度实体的调度状态。

/* 初始化红黑树根节点 */

static DEFINE_STATIC_KEY_FALSE(sched_init)[NR_CPUS];

#define rb_erase_init(node, root) \

do { INIT_HLIST_NODE(&(node)->rb_hash); (node)->rb_parent_color = 0; } while(0)

#define RB_ROOT (struct rb_root) { NULL, }

...

在这里,对红黑树的初始化是非常重要的。它使用了rb_erase_init()宏来删除和初始化一个节点,并使用RB_ROOT宏来初始化根节点。这是调度器初始化的第一步。

3.2 初始化cfs运行队列

接下来的一步是初始化CFS运行队列,它由struct cfs_rq数据结构表示。CFS运行队列用于按照优先级分配CPU时间片,并负责管理进程的调度状态。

struct cfs_rq {

/* CFS运行队列的锁 */

raw_spinlock_t lock;

...

};

/* 初始化cfs运行队列 */

static inline void init_cfs_rq(struct cfs_rq *cfs_rq)

{

raw_spin_lock_init(&cfs_rq->lock);

...

}

在这里,可以看到cfs运行队列使用一个raw spinlock来实现互斥操作,并且使用init_cfs_rq()函数来进行初始化。这是调度器初始化的第二步。

3.3 初始化调度实体

接下来的一步是初始化调度实体,也就是struct sched_entity数据结构。每个进程或线程都有一个调度实体,用于表示其调度状态。调度实体包含了进程或线程的优先级、对应的任务、调度状态等信息。

struct sched_entity {

/* 进程的优先级 */

struct load_weight load;

...

};

/* 初始化调度实体 */

static void

init_entity_runnable_average(struct sched_entity *se, int cpu)

{

memset(&se->runnable_load_avg, 0, sizeof(se->runnable_load_avg));

...

}

在这里,可以看到调度实体包含了许多重要的信息,如进程或线程的优先级、负载均衡信息等。调度实体的初始化通过init_entity_runnable_average()函数来实现。这是调度器初始化的第三步。

3.4 初始化调度域

最后一步是初始化调度域,也就是struct sched_domain数据结构。调度域用于管理不同的CPU资源,并决定如何分配CPU时间片。在初始化期间,调度域会与CFS运行队列和红黑树结合使用,以实现调度实体的分配和管理。

struct sched_domain {

/* 红黑树用于按优先级排序调度实体 */

struct rb_root rb_root;

...

};

/* 初始化调度域 */

static inline void

init_sched_domains(const struct cpumask *cpu_map_populated)

{

...

/* 初始化红黑树根节点 */

rq->curr->se.rb_node = rb_erase_init(&rq->curr->se, &rq->leaf_cfs_rq->tasks_timeline.rb_root);

...

/* 初始化调度域的红黑树 */

domain->rb_root = RB_ROOT;

...

}

在这里,可以看到调度域使用红黑树struct rb_root来排序调度实体,并使用rb_erase_init()函数来进行节点的删除和初始化。调度域的初始化由init_sched_domains()函数完成,这是调度器初始化的最后一步。

4.总结

本文深入探讨了Linux内核调度器初始化的过程和关键步骤。首先,分析了初始化red-black树的重要性。然后,介绍了CFS运行队列和调度实体的初始化流程。最后,讨论了调度域的初始化和与红黑树的结合使用。通过了解内核调度器初始化的过程,可以更好地理解其工作原理,进而深入研究和优化Linux系统的性能。

操作系统标签