MOS Lab3 进程调度实战从 PCB 初始化到 200Hz 时钟中断切换1. 进程控制块PCB的初始化与内存管理在操作系统中进程控制块PCB是描述进程状态的核心数据结构。MOS 操作系统的 PCB 结构体Env包含以下关键字段struct Env { struct Trapframe env_tf; // 寄存器保存区 LIST_ENTRY(Env) env_link; // 空闲链表指针 u_int env_id; // 进程唯一标识符 u_int env_status; // 进程状态RUNNABLE/FREE等 Pde *env_pgdir; // 页目录指针 TAILQ_ENTRY(Env) env_sched_link; // 调度队列指针 u_int env_pri; // 进程优先级 };初始化过程通过env_init()函数完成三个关键操作空闲链表构建将envs数组中的所有 PCB 插入env_free_list模板页目录创建建立内核空间到用户空间的只读映射自映射机制通过UVPT实现页目录自访问提示自映射机制使得用户进程可以通过固定虚拟地址访问自己的页表结构这是 MOS 内存管理的精妙设计。2. 进程创建与 ELF 加载进程创建的核心函数env_create()通过以下步骤实现步骤操作关键函数1分配 PCBenv_alloc()2设置优先级直接赋值env_pri3加载 ELFload_icode()4加入调度队列TAILQ_INSERT_HEADELF 加载过程中需要处理三种内存映射情况非对齐起始地址处理段起始地址不在页边界的情况完整数据加载将 ELF 段内容完整加载到内存BSS 段处理分配未初始化数据段的内存空间// ELF 加载核心逻辑示例 for (i offset; i bin_size; i BY2PG) { map_page(data, va i, 0, perm, bin i, MIN(bin_size - i, BY2PG)); } while (i sgsize) { // 处理 BSS 段 map_page(data, va i, 0, perm, NULL, BY2PG); i BY2PG; }3. 时钟中断与异常处理框架MOS 采用 200Hz 的时钟中断频率通过以下步骤初始化LEAF(kclock_init) li t0, 200 # 设置中断频率为200Hz sw t0, (KSEG1|DEV_RTC_ADDRESS|DEV_RTC_HZ) jr ra END(kclock_init)异常处理采用分级机制硬件层CPU 自动跳转到 0x80000080 异常入口分发层exc_gen_entry保存上下文后查询exception_handlers数组处理层执行具体的异常处理函数如handle_int关键数据结构void (*exception_handlers[32])(void) { [0] handle_int, // 中断处理 [2...3] handle_tlb, // TLB异常 [8] handle_sys // 系统调用 };4. 进程调度算法实现调度器schedule()采用带优先级的轮转算法其决策逻辑如下if (yield || count 0 || e NULL || e-env_status ! ENV_RUNNABLE) { // 从调度队列头部取新进程 e TAILQ_FIRST(env_sched_list); count e-env_pri; // 重置时间片 } count--; // 减少当前进程时间片进程切换的关键步骤上下文保存将寄存器状态保存到原进程的env_tf地址空间切换更新cur_pgdir全局变量新进程启动通过env_pop_tf恢复新进程上下文LEAF(env_pop_tf) sll a1, a1, 6 # 设置ASID mtc0 a1, CP0_ENTRYHI move sp, a0 # 切换到新进程的栈帧 j ret_from_exception # 异常返回 END(env_pop_tf)5. 关键调试技巧与常见问题在实验过程中以下几个调试要点值得关注自映射验证通过UVPT地址检查页表是否正确建立中断触发测试使用kclock_init后观察是否进入timer_irq进程状态监控检查env_status和调度队列状态常见问题解决方案TLB 异常检查env_pgdir是否有效设置调度死锁确认所有ENV_RUNNABLE进程都在env_sched_list中上下文保存不完整核对SAVE_ALL和RESTORE_ALL宏的实现实验中最精妙的设计在于通过 200Hz 的时钟中断实现了用户进程与内核之间的控制权切换。这种机制既保证了系统的响应性又为后续实验如虚拟内存、系统调用奠定了基础。