Linux sched_init调度器初始化与idle线程创建
Linux sched_init调度器初始化与idle线程创建sched_init是内核调度子系统的初始化入口,定义在kernel/sched/core.c中。它在start_kernel中很早被调用,负责初始化调度器的核心数据结构和为当前CPU创建idle线程。调度器使用sched_class链表管理多个调度类,每个调度类决定不同策略下进程的调度行为。函数入口如下:c// kernel/sched/core.cvoid __init sched_init(void){int i;unsigned long ptr;// 初始化调度类优先级链表// 调度类按优先级从高到低排列:// stop_sched_class - dl_sched_class - rt_sched_class// - fair_sched_class - idle_sched_classINIT_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_SCHEDrq-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.cvoid __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 RQcfs_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.cvoid __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_NORMALinit_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的rqcpu_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.cDEFINE_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/mwaitarch_cpu_idle_enter();if (!need_resched()) {// 真实的idle操作// 调用mwait或hlt降低功耗default_idle_call();}arch_cpu_idle_exit();}// 有进程需要调度,退出idletick_nohz_idle_exit();preempt_enable_no_resched();schedule_preempt_disabled();}}sched_init中另一个关键组件是负载均衡数据结构的初始化:c// kernel/sched/topology.cvoid __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功耗。