MOS Lab3 进程调度实战:从 PCB 初始化到时间片轮转的 5 个关键步骤
MOS Lab3 进程调度实战从 PCB 初始化到时间片轮转的 5 个关键步骤在操作系统的核心机制中进程调度是实现多任务并发的基石。本文将深入剖析北航操作系统课程Lab3的实验要点通过五个关键步骤带您完成从进程控制块初始化到完整时间片轮转调度的全流程实战。1. 进程控制块PCB的初始化艺术进程控制块是操作系统管理进程的核心数据结构在MOS中表现为struct Env。初始化过程始于env_init()函数这个阶段需要完成三项核心工作// 关键初始化代码示例 void env_init(void) { // 初始化空闲进程链表 LIST_INIT(env_free_list); // 初始化调度队列 TAILQ_INIT(env_sched_list); // 反向初始化所有Env结构体 for (int i NENV-1; i 0; i--) { LIST_INSERT_HEAD(env_free_list, envs[i], env_link); envs[i].env_status ENV_FREE; } // 创建模板页目录 struct Page *p; panic_on(page_alloc(p)); p-pp_ref; base_pgdir (Pde *)page2kva(p); // 设置用户空间映射 map_segment(base_pgdir, 0, PADDR(pages), UPAGES, ROUND(npage*sizeof(struct Page), BY2PG), PTE_G); map_segment(base_pgdir, 0, PADDR(envs), UENVS, ROUND(NENV*sizeof(struct Env), BY2PG), PTE_G); }关键点解析双链表初始化使用LIST_INIT和TAILQ_INIT分别初始化空闲链表和调度队列反向插入技巧通过从后向前遍历确保第一个分配的PCB是envs[0]模板页目录创建所有进程共享的基础映射结构包含内核关键数据注意模板页目录的自映射机制UVPT设置是理解用户空间访问页表的关键通过e-env_pgdir[PDX(UVPT)] PADDR(e-env_pgdir) | PTE_V实现页目录自引用。2. 进程创建的完整链路进程创建的核心函数env_create()实际上封装了三个关键操作环境分配从空闲链表获取PCB优先级设置初始化调度参数二进制加载通过ELF加载器装入程序ELF加载的详细流程步骤函数功能描述1elf_from()验证ELF文件头有效性2elf_load_seg()遍历程序头表处理每个PT_LOAD段3load_icode_mapper()实际执行页分配和内容拷贝// ELF加载关键代码 static void load_icode(struct Env *e, const void *binary, size_t size) { const Elf32_Ehdr *ehdr elf_from(binary, size); e-env_tf.cp0_epc ehdr-e_entry; // 设置入口点 ELF_FOREACH_PHDR_OFF (ph_off, ehdr) { Elf32_Phdr *ph (Elf32_Phdr *)(binary ph_off); if (ph-p_type PT_LOAD) { panic_on(elf_load_seg(ph, binary ph-p_offset, load_icode_mapper, e)); } } }三种典型内存映射情况起始地址未对齐处理首个页面的偏移部分完整页拷贝循环处理中间完整页面零填充段当p_memsz p_filesz时补充零页3. 时钟中断的初始化与触发时钟中断是驱动调度的核心机制其初始化涉及两个关键函数# kclock.S 中的时钟初始化 LEAF(kclock_init) li t0, 200 # 设置200Hz中断频率 sw t0, (KSEG1|DEV_RTC_ADDRESS|DEV_RTC_HZ) jr ra END(kclock_init) # env_asm.S 中的中断使能 LEAF(enable_irq) li t0, (STATUS_CU0|STATUS_IM4|STATUS_IEc) mtc0 t0, CP0_STATUS jr ra END(enable_irq)中断触发流程时钟硬件每1/200秒产生中断信号CPU跳转到0x80000180异常入口经过SAVE_ALL保存上下文后进入handle_int确认是4号中断后进入timer_irq处理关键细节中断响应后必须写DEV_RTC_INTERRUPT_ACK寄存器清零中断状态4. 异常处理的完整路径MOS的异常处理采用统一入口分发机制异常发生 ↓ exc_gen_entry (保存上下文) ↓ 根据CP0_CAUSE[6:2]查exception_handlers表 ↓ 跳转到对应处理函数handle_int/handle_tlb等 ↓ 执行具体处理逻辑 ↓ ret_from_exception (恢复上下文) ↓ eret返回关键数据结构void (*exception_handlers[32])(void) { [0 ... 31] handle_reserved, [0] handle_int, // 中断 [2 ... 3] handle_tlb,// TLB异常 [1] handle_mod, // 写只读页 [8] handle_sys // 系统调用 };5. 时间片轮转调度算法实现调度器的核心逻辑体现在schedule()函数中void schedule(int yield) { static int count 0; struct Env *e curenv; if (yield || count 0 || e NULL || e-env_status ! ENV_RUNNABLE) { if (e ! NULL) { TAILQ_REMOVE(env_sched_list, e, env_sched_link); if (e-env_status ENV_RUNNABLE) { TAILQ_INSERT_TAIL(env_sched_list, e, env_sched_link); } } if (TAILQ_EMPTY(env_sched_list)) { panic(schedule: no runnable envs); } e TAILQ_FIRST(env_sched_list); count e-env_pri; // 优先级即时间片长度 } count--; env_run(e); }四种触发调度的条件主动让出CPUyield1时间片耗尽count0当前进程不可运行首次调度curenvNULL进程切换三步骤保存当前上下文到PCB更新curenv和cur_pgdir通过env_pop_tf恢复新进程上下文LEAF(env_pop_tf) sll a1, a1, 6 # 将ASID左移到EntryHi正确位置 mtc0 a1, CP0_ENTRYHI move sp, a0 # 切换到新进程的栈帧 j ret_from_exception # 通过异常返回路径恢复 END(env_pop_tf)通过这五个关键步骤的配合MOS操作系统实现了完整的进程管理机制。从PCB初始化到ELF加载再到时钟中断驱动的轮转调度每个环节都体现了操作系统设计的精妙之处。