Linux sched_init调度器初始化与idle线程创建
Linux sched_init调度器初始化与idle线程创建
sched_init是内核调度子系统的初始化入口,定义在kernel/sched/core.c中。它在start_kernel中很早被调用,负责初始化调度器的核心数据结构和为当前CPU创建idle线程。调度器使用sched_class链表管理多个调度类,每个调度类决定不同策略下进程的调度行为。
函数入口如下:
```c
// kernel/sched/core.c
void __init sched_init(void)
{
int i;
unsigned long ptr;
// 初始化调度类优先级链表
// 调度类按优先级从高到低排列:
// stop_sched_class -> dl_sched_class -> rt_sched_class
// -> fair_sched_class -> idle_sched_class
INIT_LIST_HEAD(&stop_sched_class.sibling);
// ... 其他调度类链表初始化
// 等待队列哈希表初始化
waitq_hash_size = 1UL << wait_table_bits;
waitq_hash = (struct wait_queue_head *)
alloc_large_system_hash("Waitqueues",
sizeof(struct wait_queue_head),
0, wait_table_bits,
HASH_EARLY | HASH_ZERO,
NULL, NULL, 0, 0);
// 初始化每个CPU的rq(runqueue)
for_each_possible_cpu(i) {
struct rq *rq = cpu_rq(i);
raw_spin_lock_init(&rq->lock);
rq->nr_running = 0;
rq->calc_load_active = 0;
rq->calc_load_update = jiffies + LOAD_FREQ;
INIT_LIST_HEAD(&rq->cfs_tasks);
init_cfs_rq(&rq->cfs);
init_rt_rq(&rq->rt);
init_dl_rq(&rq->dl);
#ifdef CONFIG_FAIR_GROUP_SCHED
rq->cfs.root = &rq->cfs;
#endif
}
// 设置当前执行上下文为idle任务
current->sched_class = &idle_sched_class;
// 初始化idle线程的调度实体
init_task_rq(&init_task, 0);
}
// rq(per-CPU runqueue)的数据结构
struct rq {
raw_spinlock_t lock; // 运行队列锁
unsigned int nr_running; // 当前运行进程数
struct cfs_rq cfs; // CFS调度子队列
struct rt_rq rt; // 实时调度子队列
struct dl_rq dl; // Deadline调度子队列
struct task_struct *curr; // 当前运行的任务
struct task_struct *idle; // idle任务指针
struct task_struct *stop; // stop任务指针
u64 nr_switches; // 上下文切换计数
unsigned long nr_uninterruptible; // D状态进程数
};
```
CFS(完全公平调度器,Completely Fair Scheduler)是大多数进程使用的默认调度类。它的初始化在sched_init中通过init_cfs_rq完成:
```c
// kernel/sched/fair.c
void __init init_cfs_rq(struct cfs_rq *cfs_rq)
{
// 初始化CFS红黑树
cfs_rq->tasks_timeline = RB_ROOT_CACHED;
cfs_rq->min_vruntime = (u64)(-(1LL << 20));
cfs_rq->nr_running = 0;
cfs_rq->h_nr_running = 0;
// 初始化CFS统计信息
cfs_rq->exec_clock = 0;
cfs_rq->load.weight = NICE_0_LOAD;
cfs_rq->load.inv_weight = 0;
#ifdef CONFIG_FAIR_GROUP_SCHED
// 在组调度场景下设置根CFS RQ
cfs_rq->tg = &root_task_group;
root_task_group.cfs_rq[cpu] = cfs_rq;
#endif
// 初始化PELT(每个实体负载跟踪)
memset(&cfs_rq->avg, 0, sizeof(cfs_rq->avg));
}
// CFS的就绪队列关键字段
struct cfs_rq {
struct rb_root_cached tasks_timeline;
struct sched_entity *curr;
struct sched_entity *next;
struct sched_entity *last;
unsigned int nr_running;
unsigned int h_nr_running;
u64 min_vruntime;
struct load_weight load;
struct sched_statistics statistics;
};
```
idle线程的创建是sched_init中最关键的一步。在x86_64上,idle线程实际上就是起始阶段的init_task。内核通过以下方式建立idle上下文:
```c
// kernel/sched/core.c
void __init sched_init(void)
{
// 将init_task标记为idle线程
// init_task是静态定义的task_struct实例
init_task.__state = 0;
init_task.flags |= PF_IDLE;
init_task.prio = MAX_PRIO - 20;
init_task.static_prio = MAX_PRIO - 20;
init_task.normal_prio = MAX_PRIO - 20;
init_task.rt_priority = 0;
// init_task的调度策略为SCHED_NORMAL
init_task.policy = SCHED_NORMAL;
// 初始化调度类的具体实体
// CFS调度实体
init_task.se.vruntime = 0;
init_task.se.sum_exec_runtime = 0;
init_task.se.prev_sum_exec_runtime = 0;
init_task.se.nr_migrations = 0;
init_task.se.exec_start = 0;
// 实时调度实体(不参与RT调度)
init_task.rt.timeout = 0;
init_task.rt.time_slice = 0;
// 将init_task关联到CPU0的rq
cpu_rq(0)->idle = &init_task;
cpu_rq(0)->curr = &init_task;
cpu_rq(0)->idle->sched_class = &idle_sched_class;
// 初始化current指针
this_cpu_write(current_task, &init_task);
}
```
idle_sched_class是一个特殊的调度类,它只在rq中没有其他可运行进程时被选中:
```c
// kernel/sched/idle.c
DEFINE_SCHED_CLASS(idle) = {
.enqueue_task = enqueue_task_idle,
.dequeue_task = dequeue_task_idle,
.check_preempt_curr = check_preempt_curr_idle,
.pick_next_task = pick_next_task_idle,
.put_prev_task = put_prev_task_idle,
.set_curr_task = set_curr_task_idle,
.task_tick = task_tick_idle,
.balance = balance_idle,
};
// idle调度类的pick_next_task实现
static struct task_struct *
pick_next_task_idle(struct rq *rq)
{
// 直接返回rq的idle指针
// idle任务总可以被"选中"
return rq->idle;
}
// idle线程的主循环
static void do_idle(void)
{
int cpu = smp_processor_id();
// 检查是否需要重新调度
for (;;) {
tick_nohz_idle_stop_tick();
while (!need_resched()) {
// 执行CPU idle指令
// x86上使用hlt/mwait
arch_cpu_idle_enter();
if (!need_resched()) {
// 真实的idle操作
// 调用mwait或hlt降低功耗
default_idle_call();
}
arch_cpu_idle_exit();
}
// 有进程需要调度,退出idle
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule_preempt_disabled();
}
}
```
sched_init中另一个关键组件是负载均衡数据结构的初始化:
```c
// kernel/sched/topology.c
void __init sched_init_domains(const struct cpumask *cpu_map)
{
int i;
// 初始化调度域拓扑
// 调度域分为:
// SD_LEVEL_SIBLING: 同一物理CPU的兄弟核心
// SD_LEVEL_MC: 多核心
// SD_LEVEL_NUMA: NUMA节点间
// SD_LEVEL_NODE: 跨NUMA节点
// 当前只有一个CPU(BSP),建立最小的调度域
for_each_possible_cpu(i) {
if (cpumask_test_cpu(i, cpu_map)) {
// 创建该CPU的调度域层级
struct sched_domain *sd;
sd = build_sched_domain(NULL, i);
rcu_assign_pointer(per_cpu(sd_domains, i), sd);
}
}
}
// 调度域构建
struct sched_domain *build_sched_domain(struct sched_domain_topology_level *tl,
int cpu)
{
struct sched_domain *sd = NULL;
struct sched_domain *parent = NULL;
// 自底向上构建调度域
for (; tl; tl = tl->next) {
sd = sd_init(tl, cpu);
sd->parent = parent;
if (parent)
parent->child = sd;
parent = sd;
}
return sd;
}
```
sched_init完成后,调度器可以支持最基本的进程调度。此时init_task作为idle线程运行在CPU0上。后续rest_init中kernel_init完成用户空间初始化后,idle线程在CPU空闲时由调度器自动选择执行do_idle循环,通过hlt/mwait指令降低CPU功耗。
