资讯动态

HNU_ostep操作系统实验包:进程调度与虚拟内存代码实战

发布时间:2026/10/21 14:21:45 来源:尧图企业网站定制
简介HNU_ostep-操作系统实验是一套面向高校计算机专业学生的操作系统课程实践资源围绕进程管理、内存管理、文件系统与I/O系统等核心模块展开适合正在学习操作系统原理、需要动手实现内核组件的学习者。压缩包共收录2000个文件以910个C源文件和760个头文件为主体辅以Python脚本、Shell脚本、CMake与Makefile构建文件、Markdown与txt说明文档以及少量pdf、docx讲义资料整体约42.51MB。其中ostep_code目录提供实验相关源码蓝本readme.txt给出实验总览、环境搭建与任务要求附书资料则补充经典教材与讲义帮助理解操作系统内部机制。通过阅读和修改调度、内存分配等代码读者可直观观察不同算法对执行效率的影响并借助构建脚本快速复现实验环境。目前已有165人学习适合希望以实践方式夯实系统软件开发基础的学习者参考。1. 从一份操作系统实验包说起它到底能帮你解决什么如果你正在啃操作系统课大概率会遇到一个尴尬局面教材上的进程调度、虚拟内存、文件系统讲得头头是道但真让你写一个能跑的调度器或者页表管理器手就僵住了。这份 HNU_ostep 操作系统实验包就是冲着这个断层来的。它把 OSTEP 那本教材里的核心章节拆成了可编译、可运行、可验证的实验单元每个单元都配了骨架代码和测试脚本。你不需要从零搭环境也不用猜“到底要改哪个函数”拿到手就能在本地跑起来看着自己的代码把进程调度、内存分配这些黑匣子一层层剥开。适合谁正在上操作系统课、准备实验报告、或者想补上“理论懂但动手虚”这块短板的人。它不教你写操作系统内核但能让你把课本里那些抽象概念变成终端里能打印出来的真实数据。2. 实验包结构拆解从进程调度到虚拟内存的代码地图2.1 目录布局与模块划分拿到压缩包解压后你会看到一套按 OSTEP 章节组织的目录结构。常见做法是每个实验一个独立文件夹里面包含Makefile、*.c源文件、*.h头文件和一个test或tests目录。比如进程调度实验通常放在scheduler/下虚拟内存相关在vm/下文件系统在fs/下。每个文件夹的README会写明实验目标、需要修改的函数名和预期输出格式。我一般会先扫一遍Makefile看编译目标有哪些再决定从哪个实验切入。如果你是新手建议按目录编号顺序来因为后面的实验会复用前面定义的链表、队列等基础结构。2.2 核心数据结构与接口约定这套实验包最值得称道的地方是接口定义清晰。以进程调度为例骨架代码里通常有一个struct task_struct或类似的进程控制块里面包含pid、state、priority、runtime等字段。你需要实现的调度器函数会接收一个任务队列指针返回下一个要运行的任务。参数说明上runtime一般表示该任务已运行的时间片priority是静态优先级数值越小优先级越高。虚拟内存实验里你会看到struct pte页表项和struct frame物理帧的定义页表项里通常有valid、dirty、ref等标志位。这些字段不是摆设测试脚本会检查你在页面置换时是否正确更新了它们。常见做法是先把头文件里的结构体注释读一遍再动手写逻辑否则很容易出现“功能对了但标志位没置位”导致测试挂掉的情况。2.3 编译与运行环境准备实验包一般基于 Linux 环境依赖gcc、make和标准 C 库。如果你用 Windows建议在 WSL2 或者虚拟机里跑因为部分实验会用到fork()、mmap()这类系统调用。先确认gcc --version和make --version能正常输出然后进入某个实验目录执行make。如果编译报错八成是缺少某个头文件或者Makefile里的路径写死了。我遇到过Makefile里用-I./include但实际头文件在../include的情况改一下路径就行。编译通过后运行./test或make test会启动测试脚本它会打印每个测试用例的通过情况。注意有些实验的测试脚本需要 Python 3 环境来解析输出提前装好python3能省不少事。3. 进程调度实验从 FCFS 到多级反馈队列的代码落地3.1 调度器骨架与任务队列操作进程调度实验的核心是让你实现几种调度算法并对比它们的性能指标。骨架代码里通常已经定义好了任务队列的入队、出队函数你需要做的是在schedule()函数里决定下一个运行谁。以 FCFS先来先服务为例逻辑很简单直接取队列头。但代码里要注意取出任务后要更新它的state为RUNNING并把runtime累加。下面是一个典型的调度循环骨架// scheduler.c #include scheduler.h // 从就绪队列中选取下一个任务 struct task_struct *schedule(struct queue *ready_q) { if (queue_empty(ready_q)) { return NULL; // 没有可运行任务 } // FCFS: 直接取队头 struct task_struct *next queue_pop(ready_q); next-state RUNNING; return next; } // 模拟时间片推进更新任务状态 void tick(struct task_struct *curr, struct queue *ready_q) { if (curr NULL) return; curr-runtime; // 如果任务还没完成重新放回就绪队列 if (curr-runtime curr-burst) { curr-state READY; queue_push(ready_q, curr); } else { curr-state DONE; } }这段代码里queue_pop和queue_push是骨架提供的队列操作你不用自己实现。burst表示任务需要的总 CPU 时间runtime是已经运行的时间。每次tick模拟一个时间单位如果任务还没跑完就放回队列。参数说明ready_q是就绪队列指针curr是当前运行的任务。逻辑上FCFS 的缺点很明显——短任务如果排在长任务后面等待时间会很长。测试脚本会输出平均周转时间和平均等待时间你可以通过对比不同算法的输出来验证。3.2 时间片轮转与优先级调度的参数调优实现 RR时间片轮转时关键参数是时间片大小quantum。骨架里通常用一个全局变量或宏定义比如#define QUANTUM 4。你需要在tick函数里判断curr-runtime % QUANTUM 0如果是就把当前任务放回队尾重新调度。这里有个坑如果quantum设得太大RR 退化成 FCFS设得太小上下文切换开销占比高。我一般会先用quantum1跑一遍看趋势再调到 4 或 8 观察平均等待时间的变化。优先级调度则需要在schedule里遍历队列找priority最小的任务但要注意如果多个任务优先级相同应该按 FCFS 顺序选。代码上可以用一个循环遍历队列节点记录当前最小优先级和对应任务。测试脚本会检查你是否正确处理了优先级反转的情况——比如低优先级任务持有锁时高优先级任务不能抢占。3.3 多级反馈队列的实现与测试验证多级反馈队列MLFQ是这套实验里最复杂的一个。你需要维护多个就绪队列每个队列有不同的优先级和时间片。新任务进入最高优先级队列用完时间片后降到下一级。为了防止饥饿通常还会定期把所有任务提升回最高优先级。骨架代码里可能已经定义了struct mlfq包含多个queue数组和boost_interval参数。实现时schedule函数要从最高优先级队列开始找非空就取队头。tick函数里要判断当前任务是否用完当前队列的时间片如果是就降级。下面是一个简化的降级逻辑// mlfq.c void mlfq_tick(struct mlfq *sched, struct task_struct *curr) { if (curr NULL) return; curr-runtime; int level curr-level; // 检查是否用完当前级别的时间片 if (curr-runtime % sched-quantum[level] 0) { if (level MAX_LEVEL - 1) { curr-level; // 降级 } curr-state READY; queue_push(sched-queues[curr-level], curr); } else if (curr-runtime curr-burst) { curr-state DONE; } else { curr-state READY; queue_push(sched-queues[level], curr); } }参数说明sched-quantum[level]是第level级队列的时间片大小MAX_LEVEL是队列总数。逻辑上任务用完时间片就降级但没跑完的任务会重新入队。测试脚本会模拟一组任务输出每个任务的完成时间和队列变化。验证时你可以手动算一下某个任务的预期完成时间和脚本输出对比。如果对不上先检查boost_interval是否触发了优先级提升再看降级条件是否写反了。4. 虚拟内存实验页表、TLB 与页面置换的代码实现4.1 页表结构与地址翻译流程虚拟内存实验通常要求你实现一个简化的地址翻译模块。输入是一个虚拟地址输出是物理地址或缺页异常。骨架代码里会定义页表项struct pte和页目录struct page_table。地址翻译的流程是从虚拟地址中提取页号和偏移量查页表得到物理帧号再拼接偏移量得到物理地址。如果页表项valid为 0就触发缺页异常需要从磁盘加载页面。下面是一个典型的翻译函数// vm.c #include vm.h // 地址翻译返回物理地址-1 表示缺页 int translate(struct page_table *pt, int vaddr) { int page_num vaddr PAGE_SHIFT; // 提取页号 int offset vaddr (PAGE_SIZE - 1); // 提取偏移量 struct pte *entry pt-entries[page_num]; if (!entry-valid) { return -1; // 缺页 } entry-ref 1; // 设置引用位 int paddr (entry-frame_num PAGE_SHIFT) | offset; return paddr; }参数说明PAGE_SHIFT是页大小的位移量比如 4KB 页对应 12PAGE_SIZE是页大小。entry-frame_num是物理帧号。逻辑上翻译成功后要置ref位供页面置换算法使用。测试脚本会模拟一系列地址访问检查翻译结果和缺页次数。常见错误是忘记处理偏移量或者页号计算时用了错误的位移量。我一般会先打印几个地址的翻译结果手动验证一下。4.2 TLB 模拟与命中率统计TLB快表实验要求你模拟一个有限容量的 TLB并统计命中率。骨架里会定义struct tlb_entry和struct tlb你需要实现查找、插入和替换逻辑。常见做法是用 LRU 替换策略每次访问更新条目的时间戳满了就淘汰最久未使用的。下面是一个 TLB 查找和插入的示例// tlb.c #include tlb.h // 查找 TLB命中返回帧号未命中返回 -1 int tlb_lookup(struct tlb *t, int page_num) { for (int i 0; i t-size; i) { if (t-entries[i].valid t-entries[i].page_num page_num) { t-entries[i].last_access t-clock; // 更新访问时间 return t-entries[i].frame_num; } } return -1; // 未命中 } // 插入 TLB满了则替换最久未使用的条目 void tlb_insert(struct tlb *t, int page_num, int frame_num) { int victim -1; int oldest t-clock; for (int i 0; i t-size; i) { if (!t-entries[i].valid) { victim i; break; } if (t-entries[i].last_access oldest) { oldest t-entries[i].last_access; victim i; } } t-entries[victim].page_num page_num; t-entries[victim].frame_num frame_num; t-entries[victim].valid 1; t-entries[victim].last_access t-clock; }参数说明t-clock是一个全局递增计数器模拟时间。last_access越小表示越久未使用。逻辑上查找时更新访问时间插入时找无效条目或最久未使用的条目替换。测试脚本会输出 TLB 命中率你可以通过调整 TLB 大小观察命中率变化。注意TLB 命中时不需要查页表所以命中率直接影响平均访问时间。4.3 页面置换算法FIFO、LRU 与 Clock 的对比页面置换实验要求你实现 FIFO、LRU 和 Clock 三种算法并对比缺页次数。FIFO 最简单用一个队列记录页面进入内存的顺序淘汰队头。LRU 需要记录每个页面的最近访问时间淘汰最久未访问的。Clock 是 LRU 的近似用一个循环指针和引用位扫描时如果引用位为 1 就置 0 并跳过否则淘汰。下面是一个 Clock 算法的核心逻辑// swap.c #include swap.h // Clock 页面置换返回被淘汰的帧号 int clock_replace(struct clock_data *cd, int page_num) { while (1) { if (cd-frames[cd-hand].ref 0) { int victim cd-hand; cd-hand (cd-hand 1) % cd-num_frames; return victim; } cd-frames[cd-hand].ref 0; // 给第二次机会 cd-hand (cd-hand 1) % cd-num_frames; } }参数说明cd-hand是时钟指针cd-frames[i].ref是引用位。逻辑上扫描时如果引用位为 0 就淘汰否则置 0 并继续。测试脚本会模拟一组页面访问序列输出每种算法的缺页次数。我一般会先用小规模序列手动验证比如1 2 3 4 1 2 5 1 2 3 4 5然后跑大规模随机序列看趋势。常见坑是 Clock 算法里忘记更新hand指针导致死循环。5. 避坑与排查实验包里最容易翻车的五个地方5.1 编译报错“undefined reference toqueue_push”现象make时链接阶段报错提示某个队列操作函数未定义。原因骨架代码里队列实现放在单独的queue.c里但Makefile的SRCS变量没包含它。解决打开Makefile找到SRCS scheduler.c这一行改成SRCS scheduler.c queue.c或者直接用wildcard *.c。如果还不行检查头文件里函数声明是否加了extern CC 项目常见。5.2 测试脚本输出“Segmentation fault”现象运行./test直接崩溃没有输出。原因最常见的是空指针解引用比如schedule返回NULL后调用方没检查就访问curr-pid。解决在tick和schedule里加空指针判断用gdb ./test跑一遍bt看栈回溯定位到具体行。另一个可能是队列操作在空队列上pop检查queue_empty是否在pop前调用。5.3 调度结果与预期不符平均等待时间偏差大现象测试脚本输出的平均等待时间和手算结果差很多。原因时间片推进逻辑写错了比如runtime累加时机不对或者任务完成后没有从队列移除。解决在tick函数里加printf打印每个时间单位的状态和测试脚本的预期输出逐行对比。注意有些实验的burst是总 CPU 时间不是剩余时间别搞混。5.4 页面置换缺页次数异常高现象LRU 的缺页次数比 FIFO 还多不符合理论预期。原因LRU 的访问时间更新逻辑有误比如只在插入时更新查找时没更新。解决检查tlb_lookup和页表访问函数确保每次访问都更新last_access或ref位。另外如果页面大小设置不对比如PAGE_SHIFT写成了 10 而不是 12会导致页号计算错误缺页次数飙升。5.5 多级反馈队列出现任务饥饿现象低优先级任务一直得不到运行测试超时。原因没有实现优先级提升机制或者boost_interval设置得太大。解决在mlfq_tick里加一个全局计数器每经过boost_interval个时间单位就把所有任务移回最高优先级队列。参数上boost_interval一般设为 50 到 100 个时间单位太小会导致优先级频繁重置太大则饥饿依旧。6. 进阶技巧用脚本自动化验证与性能对比跑通单个实验只是第一步真正省时间的是把多个算法的输出自动化对比。我习惯写一个 Python 脚本调用make编译不同版本然后解析测试输出生成对比表格。比如下面这个脚本可以同时跑 FCFS、RR 和 MLFQ提取平均周转时间# compare.py import subprocess import re algorithms [fcfs, rr, mlfq] results {} for algo in algorithms: # 编译对应版本 subprocess.run([make, fALGO{algo}], checkTrue) # 运行测试并捕获输出 out subprocess.run([./test], capture_outputTrue, textTrue).stdout # 提取平均周转时间 match re.search(rAvg Turnaround: ([\d.]), out) if match: results[algo] float(match.group(1)) # 打印对比表格 print(f{Algorithm:10} {Avg Turnaround:15}) for algo, tat in results.items(): print(f{algo:10} {tat:15.2f})这段脚本的关键是Makefile里支持ALGO变量切换编译目标比如CFLAGS -DALGO_$(ALGO)然后在代码里用#ifdef选择不同调度器。参数说明subprocess.run的checkTrue会在编译失败时抛异常capture_outputTrue捕获标准输出。逻辑上脚本自动编译、运行、解析、对比省去手动改代码的麻烦。我一般还会加一个--repeat参数跑多次取平均值减少随机性影响。另一个进阶技巧是给测试脚本加超时保护。有些实验在死循环时不会自己退出用timeout 10 ./test可以强制终止。如果超时说明调度逻辑有死循环优先检查while循环的退出条件。从那以后我每次跑实验都强制走一遍timeout省得终端卡死。希望这些经验能帮你少走点弯路。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑