第十一篇:实现多线程调度
线程调度分为三个步骤
- 时钟中断处理函数
- 调度器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