MOS Lab3 进程创建与调度:从 PCB 初始化到 200Hz 时钟中断的完整流程解析
MOS Lab3 进程创建与调度从 PCB 初始化到 200Hz 时钟中断的完整流程解析1. 进程管理的核心架构在操作系统中进程作为资源分配的基本单位其管理机制直接影响系统的稳定性和效率。MOS Lab3 实验构建了一个完整的进程管理体系核心组件包括进程控制块(PCB)每个进程对应一个struct Env结构体记录进程状态、内存映射、寄存器快照等关键信息调度队列通过env_sched_list尾队列管理就绪进程实现优先级轮转调度中断机制200Hz 时钟中断触发调度器运行实现多进程分时复用实验中的关键数据结构关系如下图所示示例struct Env { struct Trapframe env_tf; // 寄存器上下文 LIST_ENTRY(Env) env_link; // 空闲链表指针 u_int env_id; // 唯一进程ID u_int env_status; // 状态(ENV_FREE/RUNNABLE/NOT_RUNNABLE) Pde *env_pgdir; // 页目录基址 TAILQ_ENTRY(Env) env_sched_link; // 调度队列指针 u_int env_pri; // 优先级(时间片长度) };2. 进程创建全流程2.1 环境初始化阶段内核启动时通过mips_init()完成关键初始化void mips_init() { env_init(); // 初始化PCB空闲链表和调度队列 ENV_CREATE_PRIORITY(user_bare_loop, 1); // 创建优先级1的进程 ENV_CREATE_PRIORITY(user_bare_loop, 2); // 创建优先级2的进程 kclock_init(); // 设置200Hz时钟中断 enable_irq(); // 开启中断响应 }env_init()函数执行以下关键操作初始化两个链表结构LIST_INIT(env_free_list); // 空闲PCB链表 TAILQ_INIT(env_sched_list); // 调度队列将所有PCB插入空闲链表状态设为ENV_FREE创建模板页目录建立内核空间到用户空间的只读映射2.2 进程控制块分配env_alloc()从空闲链表获取PCB并初始化// 关键汇编实现片段 env_alloc: lw t0, env_free_list # 获取空闲链表头 beqz t0, no_free_env # 检查是否有空闲PCB lw t1, ENV_LINK(t0) # 获取下一个空闲PCB sw t1, env_free_list # 更新链表头 jal env_setup_vm # 设置进程地址空间内存管理关键点每个进程拥有独立的页目录(env_pgdir)通过自映射机制实现用户空间页表访问e-env_pgdir[PDX(UVPT)] PADDR(e-env_pgdir) | PTE_V;2.3 ELF程序加载load_icode()完成用户程序加载解析ELF头部验证文件格式遍历程序头表加载所有PT_LOAD段使用回调机制实现灵活的内存映射typedef int (*elf_mapper_t)(void *data, u_long va, size_t offset, u_int perm, const void *src, size_t len);典型段加载过程for (i 0; i ph-p_memsz; i BY2PG) { map_page(env, ph-p_vaddr i, i ph-p_filesz ? ph-p_offset i : 0, perm, i ph-p_filesz ? binary ph-p_offset i : NULL, MIN(ph-p_filesz - i, BY2PG)); }3. 时钟中断与进程调度3.1 中断初始化流程时钟中断设置分为两个阶段频率设置通过MMIO写入RTC设备kclock_init: li t0, 200 # 200Hz频率 sw t0, (KSEG1|DEV_RTC_ADDRESS|DEV_RTC_HZ)中断使能配置CP0状态寄存器enable_irq: li t0, (STATUS_CU0 | STATUS_IM4 | STATUS_IEc) mtc0 t0, CP0_STATUS3.2 中断处理链路当时钟中断发生时CPU执行以下处理流程硬件自动跳转到0x80000080异常入口保存上下文到内核栈SAVE_ALL宏通过异常向量表分发到handle_int检查中断源处理时钟中断handle_int: mfc0 t0, CP0_CAUSE mfc0 t2, CP0_STATUS andi t1, t0, STATUS_IM4 bnez t1, timer_irq3.3 调度器实现schedule()函数实现优先级轮转调度void schedule(int yield) { static int count 0; // 剩余时间片 struct Env *e curenv; if (yield || count 0 || !e || e-env_status ! ENV_RUNNABLE) { if (e) { 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); } } e TAILQ_FIRST(env_sched_list); count e-env_pri; // 重置时间片 } count--; env_run(e); }调度触发条件对照表条件类型触发场景处理方式主动让出调用sys_yield()当前进程移到队列尾部时间片耗尽count减至0选择新进程并重置时间片进程阻塞env_status变化移出调度队列首次调度curenv为NULL选择队列首进程4. 进程上下文切换env_run()完成进程运行环境切换保存当前上下文到PCB若存在if (curenv) { curenv-env_tf *((struct Trapframe *)KSTACKTOP - 1); }更新当前进程指针和页目录curenv e; cur_pgdir e-env_pgdir;恢复目标进程上下文env_pop_tf: sll a1, a1, 6 # 设置ASID mtc0 a1, CP0_ENTRYHI move sp, a0 # 指向目标Trapframe j ret_from_exception关键寄存器恢复过程通过RESTORE_SOME宏恢复通用寄存器从env_tf.cp0_epc恢复程序计数器rfe指令返回到用户模式5. 实战调试技巧5.1 常见问题排查页表错误检查env_setup_vm中的自映射设置// 正确配置示例 e-env_pgdir[PDX(UVPT)] PADDR(e-env_pgdir) | PTE_V;调度异常验证时间片更新逻辑// 在schedule()中确保正确重置 count e-env_pri; // 而非固定值上下文保存确认SAVE_ALL和RESTORE_SOME宏对齐5.2 关键调试点中断触发频率验证# 在GXemul中检查时钟中断计数 gxemul -E testmips -C R3000 -d os.img -T 60进程状态监控// 添加调试输出 printk(Schedule: curpid%d, nextpid%d, count%d\n, curenv ? curenv-env_id : -1, e-env_id, count);内存映射检查工具void print_pgdir(Pde *pgdir) { for (int i 0; i 1024; i) { if (pgdir[i] PTE_V) { printk(PD[%03d] 0x%08x\n, i, pgdir[i]); } } }