第十一篇:实现多线程调度
线程调度分为三个步骤
- 时钟中断处理函数
- 调度器schedule
- 任务切换函数switch_to
注册时钟中断处理函数
时间中断处理函数的作用是判断当前进程是否还有时间片,如果没有则进行调度。
void intr_timer_handler(void) { struct task_struct* cur_thread = running_thread(); //检查栈是否溢出 ASSERT(cur_thread->stack_magic == 0x20030621); //当前进程总时间数+1 cur_thread->elapsed_ticks++; //总的时钟中断数+1 ticks++; //时间片用完了则调度新的进程 if (cur_thread->ticks == 0) { schedule(); } else { cur_thread->ticks--; } }
实现调度器
调度器负责找到当前进程的pcb和下一个待运行进程的pcb,并调用任务切换函数实现线程的切换。
//实现任务调度 void schedule(void) { ASSERT(get_intr_status() == INTR_OFF); struct task_struct* cur = running_thread(); if (cur->status == TASK_RUNNING) { //将当前线程加入队列 ASSERT(!node_find(&thread_ready_list, &cur->general_tag)); list_append(&thread_ready_list, &cur->general_tag); cur->ticks = cur->priority; cur->status = TASK_READY; } else { //若当前线程需要某事发生后才能继续上cpu运行,那么不需要加入就绪队列 } //获取下一个待运行的线程,并切换到cpu上 ASSERT(!list_empty(&thread_ready_list)); thread_tag == NULL; thread_tag = list_pop(&thread_ready_list); struct task_struct* next = elem2entry(struct task_struct, general_tag, thread_tag); next->status = TASK_RUNNING; switch_to(cur, next); }
实现任务切换函数
任务切换函数的功能是保持当前进程的上下文,并恢复下一个进程的上下文。并修改eip,实现控制流的转移。
[bits 32] section .text global switch_to switch_to: ;栈中此处是返回地址 push esi push edi push ebx push ebp ;得到cur的地址 mov eax, [esp + 20] ;保存cur的栈指针 ;pcb的偏移0处是self_kstack mov [eax], esp ;以上是保存cur的环境,接下来恢复next的环境 ;得到next的地址 mov eax, [esp + 24] ;恢复栈指针 mov esp, [eax] ;恢复寄存器 pop ebp pop ebx pop edi pop esi ret