主要内容:掌握ChChor的内核调度策略与实现。
调度相关的数据结构 thread_ctx
线程是调度的基本单元,因此thread_ctx中保存有调度相关的运行上下文、调度上下文、运行状态等信息。
budget:rr调度器中的时间片
sched_ops
调度器的ops是一组抽象接口,不同的调度器都使用这一组接口
thread_state
调度器初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void main (void *addr) { ... kernel_lock_init(); lock_kernel(); sched_init(&rr); ... sched(); eret_to_thread(switch_context()); }
idle线程初始化 1 2 3 4 5 6 7 8 9 10 int sched_init (struct sched_ops *sched_ops) { BUG_ON(sched_ops == NULL ); cur_sched_ops = sched_ops; cur_sched_ops->sched_init(); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int rr_sched_init (void ) { int i = 0 ; for (i = 0 ; i < PLAT_CPU_NUM; i++) { current_threads[i] = NULL ; init_list_head(&rr_ready_queue[i]); } for (i = 0 ; i < PLAT_CPU_NUM; i++) { BUG_ON(!(idle_threads[i].thread_ctx = create_thread_ctx())); init_thread_ctx(&idle_threads[i], 0 , 0 , MIN_PRIO, TYPE_IDLE, i); arch_idle_ctx_init(idle_threads[i].thread_ctx, idle_thread_routine); idle_threads[i].vmspace = NULL ; } kdebug("Scheduler initialized. Create %d idle threads.\n" , i); return 0 ; }
初始化全局变量
为每一个核创建idle线程,并加到RQ
创建idle线程的上下文,主要是创建kernel stack
初始化idle线程的上下文
线程调度 rr_sched 挑选一个thread来执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 int rr_sched (void ) { if (current_thread != NULL && current_thread->thread_ctx != NULL && current_thread->thread_ctx->sc != NULL && current_thread->thread_ctx->sc->budget != 0 ) { return 0 ; } if (current_thread != NULL ) { rr_sched_enqueue(current_thread); } struct thread *target_thread ; if ((target_thread = rr_sched_choose_thread()) == NULL ) { return -EINVAL; } rr_sched_refill_budget(target_thread, DEFAULT_BUDGET); return switch_to_thread(target_thread); }
rr_sched_enqueue 一个线程切出,重新放到RQ即可。这里涉及到了亲和性的概念,也就是线程是否要执行在哪个cpu执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 int rr_sched_enqueue (struct thread *thread) { if (thread == NULL || thread->thread_ctx == NULL || thread->thread_ctx->state == TS_READY) { return -EINVAL; } if (thread->thread_ctx->type == TYPE_IDLE) { return 0 ; } u32 cpu_id = smp_get_cpu_id(); if (thread->thread_ctx->affinity != NO_AFF) { cpu_id = thread->thread_ctx->affinity; if (cpu_id >= PLAT_CPU_NUM) { return -EINVAL; } } list_append(&thread->ready_queue_node, &rr_ready_queue[cpu_id]); thread->thread_ctx->state = TS_READY; thread->thread_ctx->cpuid = cpu_id; return 0 ; }
rr_sched_choose_thread 选择一个线程来执行,从RQ队首出队即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct thread *rr_sched_choose_thread (void ) { u32 cpu_id = smp_get_cpu_id(); if (list_empty(&(rr_ready_queue[cpu_id]))) { return &(idle_threads[cpu_id]); } struct thread *chosen_thread = list_entry(rr_ready_queue[cpu_id].next, struct thread, ready_queue_node); if (rr_sched_dequeue(chosen_thread) < 0 ) { return NULL ; } return chosen_thread; }
rr_sched_dequeue 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int rr_sched_dequeue (struct thread *thread) { if (thread == NULL || thread->thread_ctx == NULL || thread->thread_ctx->state != TS_READY) { return -EINVAL; } if (thread->thread_ctx->type == TYPE_IDLE) { return 0 ; } list_del(&thread->ready_queue_node); thread->thread_ctx->state = TS_INTER; return 0 ; }
switch_to_thread 1 2 3 4 5 6 7 8 9 int switch_to_thread (struct thread *target) { target->thread_ctx->cpuid = smp_get_cpu_id(); target->thread_ctx->state = TS_RUNNING; smp_wmb(); current_thread = target; return 0 ; }
switch_context 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 u64 switch_context (void ) { struct thread *target_thread ; struct thread_ctx *target_ctx ; target_thread = current_thread; BUG_ON(!target_thread); BUG_ON(!target_thread->thread_ctx); target_ctx = target_thread->thread_ctx; if (target_thread->thread_ctx->type != TYPE_IDLE && target_thread->thread_ctx->type != TYPE_KERNEL && target_thread->thread_ctx->type != TYPE_TESTS) { BUG_ON(!target_thread->vmspace); switch_thread_vmspace_to(target_thread); } return (u64)target_ctx->ec.reg; }
切换上下文就是把vmspace切换到目标现成的地址空间,然后返回目标线程的上下文。
最后由eret_to_thread完成处理器上下文切换。
rr_sched_handle_timer_irq 时间片减一。
1 2 3 4 5 6 7 void rr_sched_handle_timer_irq (void ) { if (current_thread != NULL && current_thread->thread_ctx->sc->budget > 0 ) { current_thread->thread_ctx->sc->budget--; } }
rr调度器使用一个定时器来做时间片的更新,在定时器终端中budget减1,然后执行调度,决定是否要切换到新的线程来执行。