操作系统实验包全解析:进程调度、内存管理与文件系统模拟
简介这份面向西南科技大学计算机相关专业学生的操作系统实验资源包涵盖进程管理、内存管理、文件管理三大核心模块适合初学操作系统课程、需要完成配套上机实验的本科生使用。压缩包共10个文件以C/C源代码.cpp/.c和可执行程序.exe为主另含文件系统相关材料既可直接运行查看效果也可阅读源码理解实现思路。文件按实验一、二、三分别组织便于对照练习。资源大小仅481KB轻量易下载。已有89人学习说明其具备一定的参考价值。通过学习这份材料读者可以直观看到进程创建/调度、内存分配策略、文件系统操作等经典实验的代码实现与可运行程序帮助快速掌握操作系统关键机制并借鉴实验报告的组织方式完成自己的课程任务。1. 操作系统实验包是什么三份源码、三组 exe 与你要理解的调度模拟我拆开“西南科技计算机操作系统实验.7z”这个压缩包后第一反应是这基本就是操作系统课程设计的标准交付物。里面没有复杂的环境依赖也没有图形界面就是三个文件夹分别对应操作系统教学里最核心的三大模块——进程管理、内存管理、文件管理。每个模块都给了 .cpp 源码和编译好的 .exe 成品这意味着你不需要从零搭工程就能跑出调度结果然后把源码读明白、改几个参数写进实验报告里交差。这套资源适合两类人。一类是有实验截止日期、正在对着汤小丹版教材复习的在校生你需要尽快知道代码怎么编译、怎么输入、输出日志怎么解释另一类是自学操作系统原理、想看看教材里的 PCB、页表、FAT 表到底怎么用 C 语言实现的人。包里全是控制台程序属于典型的教学型模拟不涉及真实内核因此读源码比折腾环境更划算。下面我就按解压还原、逐模块拆解、避坑顺序把这份资源的全貌讲清楚。2. 拆开 7z 后的工程现状文件清单、编译命令与最小运行环境2.1 压缩包结构源码、exe 与 test.c 分别对应什么解压后第一眼看到的是三个目录命名很直白实验一-进程管理、实验二-内存管理、实验三-文件管理。前两个目录各有两个文件进程管理实验.cpp、进程管理实验.exe以及内存管理.cpp、内存管理.exe。第三个目录文件最多里面有文件管理.cpp、文件管理.exe、文件管理2.cpp、文件管理2.exe、test.c还有一个名为文件系统的子目录。按我拆这类课程资源的经验带“2”后缀的很可能是第二版实现写报告时建议以文件管理2.cpp 作为主版本文件管理.cpp 留作对照。test.c 是独立的辅助验证程序一般负责打印目录结构或调用文件系统接口不属于主程序。文件清单和用途整理如下文件在实验中的角色使用建议进程管理实验.cpp / .exe进程创建、状态转换、调度输出直接编译运行重点看 PCB 数组内存管理.cpp / .exe空闲分区分配、内存回收输入请求大小后观察分配结果文件管理.cpp / .exe文件操作模拟的第一版可作为对照版本文件管理2.cpp / .exe第二版文件管理主程序报告优先引用此版本文件系统/子目录FAT 表与目录项实现打开后逐个文件排查test.c辅助验证脚本单独编译用来交叉检查每份 exe 都是 Windows 下预编译的。你能否直接双击运行取决于系统架构、杀毒软件是否拦截、运行时 DLL 是否缺失。最稳妥的操作不是双击而是先在自己机器上重新编译一次这样手里的链路就是“源码 → 可执行文件 → 运行结果”答辩时老师问什么都能对上。最小工具链只需要两样7-Zip 用于解压MinGW-w64 的 gcc/g 用于编译。你如果装了 Visual Studio 或 Dev-C 也可以只是编译参数要对得上。2.2 最小运行路径解压、重编译、重定向输出三步走先给压缩包做完整性验证。7z 文件在下载过程中偶尔会损坏宁可先花十秒执行测试也别等到运行代码时才报错。Windows 命令行下装了 7-Zip 后执行7z t 西南科技计算机操作系统实验.7z 7z x 西南科技计算机操作系统实验.7z -oD:\os_labs -y第一条是测试只检查压缩包 CRC 和头信息第二条是解压到 D:\os_labs-y 参数遇到同名文件直接覆盖。中文名压缩包解压时我习惯把目标路径换成短英文目录比如 D:\os_labs。路径套太多层中文目录时g 后续很容易报“No such file or directory”这个坑后面单独说。如果你在 Linux 或 WSL 环境下操作7z 命令本身一样用 p7zip 即可-o 参数后面不要写空格。解压完成后按扩展名区分编译方式。.cpp 后缀统一用 gtest.c 用 gcc。命令如下cd /d D:\os_labs\实验一-进程管理 g 进程管理实验.cpp -o 进程管理实验_rebuilt.exe cd /d D:\os_labs\实验二-内存管理 g 内存管理.cpp -o 内存管理_rebuilt.exe cd /d D:\os_labs\实验三-文件管理 g 文件管理2.cpp -o 文件管理2_rebuilt.exe gcc test.c -o test_rebuilt.exe每条命令都在当前目录生成带 _rebuilt 后缀的 exe不覆盖原版。这样做的好处是如果编译后的程序和原版 exe 行为不一致你可以定位是编译器差异还是代码本身的缺陷。这里的编译参数没有加 -Wall课程代码经常有未使用的变量警告加不加不影响输出。想严格一点可以自己加对结果无副作用。运行阶段有一个关键习惯不要双击。课程的 .cpp 里如果有 scanf 或 getchar 等待输入双击后窗口会在输出消失前直接关闭。正确做法是在 cmd 里运行并把标准输出和标准错误同时重定向到文件再打开日志查看。以进程管理实验为例cd /d D:\os_labs\实验一-进程管理 进程管理实验_rebuilt.exe run_output.txt 21 type run_output.txt如果需要交互输入把输入也准备好echo 5 3 1 input.txt 进程管理实验_rebuilt.exe input.txt run_output.txt 21这里的 5 3 1 是我假设的进程数、时间片和优先级数量实际参数必须对照源码 main 函数里的 scanf 格式不能凭空估计。乱给输入只能看到异常退出第五节会详细说如何从源码反推输入格式。3. 进程管理实验PCB 结构、调度循环与报告里该写的三个结论3.1 源码主干静态 PCB 表与时钟循环的模拟方式进程管理最核心的东西只有一个就是进程控制块 PCB。这份实验的源码规模不大常见写法是在文件顶部定义一个结构体数组当作进程表然后用一个循环模拟时钟中断。每次循环扫描进程表找出当前可运行的进程让它运行一个时间片更新状态和时间计数。打开 .cpp 后你会看到类似这样的定义// 进程管理实验源码中的核心结构对应你自己的 PCB 设计 #define MAX_PROC 10 typedef struct pcb { int pid; // 进程号从 0 开始每次创建加 1 char name[16]; // 进程名用于命令行日志输出 int priority; // 优先级数值小表示优先 int need_time; // 完成所需 CPU 时间片数 int used_time; // 已经占用 CPU 的时间片数 int state; // 0 就绪 1 运行 2 阻塞 } PCB; PCB pcb_table[MAX_PROC]; // 进程表容量写死课程实验够用 int proc_count 0; // 当前进程数量代码不是让你原封不动抄回报告而是理解它的设计理由。用静态数组而不是动态链表是因为课程实验阶段不需要承受高并发的内存分配压力用固定大小的表结构调试时可以直接观察数组中每个元素的状态变化老师验收代码时也容易跟算法描述对齐。写报告时“采用静态 PCB 数组保存进程控制块容量限定为 10”这句话本身就能体现你对数据结构与调度关系的理解。调度循环的常见做法是先在主函数里调用一个 create_process 函数把若干进程填入 pcb_table然后进入 while 循环。循环体做三件事扫描进程表找出 state 为就绪且优先级最高的进程把它标记为运行need_time 减 1、used_time 加 1如果 need_time 归零进程转为终止否则回到就绪态。这个状态转换的时序就是实验的关键逻辑。3.2 改哪些参数能改变运行结果进程数、优先级、时间片拿到这份实验后最值得动手的地方不是重写整套逻辑而是找到那几个敏感参数改完之后运行结果有明显变化写报告也有素材。第一个是 MAX_PROC它决定创建进程数量的上限。把 10 改成 3你会发现创建进程超过上限后不报错只是静默忽略这个行为写进实验分析反而是加分项。第二个是优先级字段 priority很多课程代码把 priority 初始化为随机数随机种子不同每次运行的输出顺序就不同。如果你想知道某一次输出为什么是 3、1、2 而不是 1、2、3直接给 srand 传入固定参数比如 srand(1)输出就完全可复现这也是答辩时证明你理解随机性的一个技巧。第三个是时间片常量。源码里通常有这个定义const int TIME_SLICE 2; // 每个进程一次运行的时间片单位把它改成 1调度顺序会变成严格轮转进程切换更频繁改成 5一个进程连续占用 CPU 的时间变长其他进程的等待时间明显增加。实验报告里最理想的展示方式是保持其他参数不变只改 TIME_SLICE截两到三张运行结果图对比同一个进程组的完成顺序。这比贴一整段代码有说服力得多。还要注意一个容易写错的概念这份代码用的是抢占式还是非抢占式优先级调度。判断方法很简单看调度循环里如果新到达的进程 priority 比当前运行进程高是否立刻切换。如果每次循环开始都重新扫描一次就绪队列那是抢占式如果必须等当前进程的时间片用完才重新选择那是非抢占式。把结论写成一句“本实验采用非抢占式静态优先级调度”或“采用时间片轮转与优先级结合”比笼统说“实现了进程调度”更有技术含金量。3.3 把源码讲给老师听状态转换与调度算法对应验收前自己把输出日志和算法对应上是最容易被忽略的一步。课程代码的打印输出通常出现在状态切换的地方格式类似“PID 1: RUNNING - READY”。你要能顺着代码解释它没有完成 need_time时间片耗尽调度器把 used_time 累加后放回就绪队列。最容易翻车的是阻塞态。真实操作系统里阻塞是因为等待 I/O 或锁但教学模拟代码里一般没有真正的 I/O 设备阻塞状态可能是随机数模拟的或者是收到某个指令后切换的。回答老师时只描述“本实验用随机数模拟了阻塞事件”就够了不要扯到中断向量和设备驱动那些不在本实验范围内。运行输出里的每一行都应该能对应到调度算法的步骤。比如优先级高的进程先完成说明是静态优先级生效某个低优先级进程长时间得不到调度说明没有做老化处理。这些观察都可以作为报告的“实验分析与思考”。围绕进程管理实验最稳妥的三个结论是这样的第一本实验用 PCB 数组加优先级比较实现了非抢占调度或抢占调度的逻辑过程第二通过修改时间片长度可以观察完成顺序变化理解时间片对响应时间和吞吐量的影响第三阻塞状态是没有真实 I/O 的模拟实际内核中需要等待队列和中断唤醒机制。把这三个方向写透进程管理模块的答辩材料就足够了。4. 内存管理与文件管理分配策略、FAT 表与辅助验证程序4.1 内存管理实验空闲分区表和首次适配的取舍逻辑内存管理实验的核心是一个空闲分区链表和若干分配函数。运行后通常先打印总内存大小然后等待你输入请求大小程序从空闲分区链表里找满足条件的区域分配成功后再打印当前分区状况。这个过程本质上是动态分区分配和教材里的可变分区是一回事。下面这段结构是这类实验最常见的组织方式我一般先看它再判断作者用的是哪种策略// 内存管理实验中的空闲分区管理节点 typedef struct free_block { long start_addr; // 分区起始地址 long size; // 分区大小单位 KB struct free_block *next; } FREE_BLOCK; FREE_BLOCK *free_list_head NULL; // 空闲分区链表头 // 首次适配分配找第一个 size 满足请求的空闲分区 FREE_BLOCK *first_fit(FREE_BLOCK *head, long request) { FREE_BLOCK *p head; while (p ! NULL) { if (p-size request) return p; // 分配这块剩余部分作为新节点挂回链表 p p-next; } return NULL; // 没有满足的返回分配失败 }首次适配在所有分配策略里实现最简单从头扫描链表找到第一个够大的分区就切。优点是请求大内存块时不容易失败缺点是不停分配回收后链表里的小碎片会越来越多。如果源码里改成记录最小可满足块那就是最佳适配能减少大分区被不断切割但要遍历整个链表查找成本高。实验报告里对比一句话写清楚就行首次适配速度快但外部碎片多最佳适配碎片控制好但查找代价大。看完这段代码后实际跑一次请求序列看输出日志里剩余空闲块的碎片数量就能判断出这份源码用的是哪种策略。内存分配的“分配失败”也有讲究。首次适配返回 NULL 时程序输出“内存不足”或“Allocation Failed”然后继续等待下一条输入。你需要知道这个“内存不足”只是空闲链表里没有满足条件的连续分区而不是操作系统真正拒绝了这个进程。真实系统会通过请求页表、虚拟内存扩展等手段处理不会直接拒绝。把这个对比写进实验报告比反复计算 1024KB 减去已分配大小要有说服力得多。4.2 交互输入与越界表现请求参数怎么给、异常输出怎么看内存管理程序运行后通常会先打印初始内存大小比如 Total memory: 1024 KB然后光标停在命令行等待输入。输入格式常见有两种一种是单次请求输入 200 表示申请 200KB另一种是连续请求每行一个操作和大小直到输入 0 退出。你必须先看 main 函数里的 scanf 和循环控制条件确定是哪种设计。如果程序是输入一次就打印一次当前空闲分区表那就把一次运行拆成多次输入每次记录分配结果和剩余分区。最容易被问到的是越界请求。输入一个比总内存还大的数比如 2048程序大概率输出“内存不足”并继续等待下一条输入。这里要留意边界输入 0 或负数时如果你看到程序死循环或输出异常通常是 while(p ! NULL) 或 while(p-size request) 里没有做参数合法性校验。这属于源码本身的缺陷不是你的环境问题。修复方式是在读入 request 后加一个 if (request 0) continue;一行代码就能解决也可以作为报告里的“实验改进”内容。4.3 文件管理实验FAT 链、目录项与 test.c 的辅助角色文件管理实验的核心不是真的去读写磁盘而是用一个一维数组模拟磁盘块再用另一个数组模拟 FAT 表记录每个文件占用的块号链。文件系统目录下的代码就是这套模拟的主干。目录项结构通常是这样的// 文件系统子目录中的目录项短文件名格式 #define NAME_LEN 12 typedef struct dir_entry { char name[NAME_LEN]; // 文件名 int start_block; // 文件起始磁盘块号 unsigned int file_len; // 文件总长度单位字节 int attr; // 0 代表普通文件1 代表目录 } DIR_ENTRY; // FAT 表fat[i] 存放第 i 块的下一个块号-1 表示文件结束 int fat[MAX_DISK_BLOCK];文件管理实验的标准操作流程是创建文件、写入内容、读取内容、删除文件每一步观察 FAT 表中的块号变化。我一般会重点看删除操作删除文件后它占用的块在 FAT 表里是否被清零。如果源码里删除时只把目录项的状态改了没有回收 FAT 块说明这份代码只实现了文件删除的逻辑层没做磁盘块回收这是可以写进“实验体会”的改进点。test.c 在这个实验里通常是独立的辅助工具用来查看磁盘块镜像或目录表内容。单独编译运行它输出的目录项可能与主程序显示一致也可能不完全一致。遇到这种情况优先相信主程序因为 test.c 很可能是出题方用于自动化检查的脚本。你在报告里说明“使用 test.c 对 FAT 表做了交叉验证”比反复贴运行日志更显功夫。5. 避坑排查解压乱码、编译环境与运行时闪退的五个现实问题5.1 解压与编码问题乱码、CRC 报错和长路径先讲第一个坑解压后文件名和源码注释乱码。现象是运行 7z 后文件夹名变成乱码源码里中文注释变成“锟斤拷”代码本身能打开但字符串字面量里的中文可能被编译器判定为非法字符。原因多半是压缩包在打包时使用了 GBK 或 GB2312 编码而你的 Windows 系统设置了解压后按 UTF-8 显示。解决方法是把源码文件用 Notepad 或 VS Code 批量转回 UTF-8或者把系统中“Beta 版 Unicode UTF-8 支持”关闭后重新解压。我的习惯是解压后先不碰代码直接看几个带中文注释的文件乱码就立刻批量转码免得改代码改到一半才发现注释错乱。第二个坑是长路径和中文路径导致编译失败。现象是 cmd 窗口里 g 报“No such file or directory”但 dir 命令明明能看到文件。原因通常是路径中嵌套了过深的中文目录或者某个文件夹名带了空格。解决方法是把整个实验包解压到 D:\os_labs 这种短英文路径下再执行编译命令。课程实验代码的 include 语句也常常是相对路径深中文目录会让相对路径解析失败这类问题改路径是最快的补救。第三个和压缩包本身有关7z 解压报错提示文件尾部和 CRC 不一致。这类现象大部分不是密码问题而是下载过程中数据不完整或磁盘扇区故障。可以用 7z t 命令先验证一次完整性如果报错就重新下载压缩包不要反复尝试解压。确认压缩包完整后再操作能省下大量排查环境的时间。5.2 编译与运行问题闪退、旧 exe 被误杀和失效行为第四个坑双击 exe 闪退看不到输出。很多同学第一次跑课程代码时双击进程管理实验.exe黑窗口闪一下就没了。这通常不是程序崩溃而是 main 函数运行完控制台窗口自动关闭。解决方法是不要在资源管理器里双击而是打开 cmd切到目录后手动执行把输出重定向到文件里再看。如果只是演示可以在源码末尾加 system(pause)但我不推荐这条路径因为老师用脚本批量跑实验时带 pause 的程序会僵在交互状态影响自动化验收。第五个坑杀毒软件把 exe 当作威胁清理。Windows Defender 和部分第三方杀毒软件对“从网络下载的未签名 exe”默认提示风险甚至直接隔离。你重新编译生成的 _rebuilt.exe 也不一定能通过因为编译出来的 PE 文件没有数字签名。解决方法是把实验目录加入杀软信任区或者直接用源码编译版不要依赖原版 exe。重编译既是学习过程也绕开了信任问题一举两得。第六个坑也值得提改完源码后运行发现输出还是旧版本。现象是你在 VS Code 里改了进程管理实验.cpp回到命令行重新 g再跑 exe结果和之前一模一样。原因是你可能没有重新编译或者编译命令失败但你没注意终端的错误输出。解决方法是重编译前用 dir 查看 exe 的修改时间确认时间戳更新了再运行。编译失败时终端通常会打印 error 信息如果只看到了命令回显没看到错误把 stderr 重定向到文件再排查。6. 把复现变成答辩素材三个验证动作和一个自查习惯先做一件事解压后把三个原始 exe 分别复制一份改名保存为 .orig 后缀然后再编译自己的 _rebuilt.exe。这样任何时候做对比你手里都有两组参考原版行为和新编译行为。如果两者输出一致说明环境没有问题如果输出不同说明你机器上的编译器版本或运行时影响了结果可以提前排查。这个备份习惯成本很低但能避免“改错了一个变量后面一整个下午都在对 diff”的局面。第二个验证动作是用临界输入测边界。内存管理实验输入 0 或负数进程管理实验创建进程数超过上限文件管理实验重复删除同一个文件。很多课程代码在边界输入下会死循环或崩溃而这也是老师在验收时最常用的测试手段。你提前跑一遍把现象和原因记下来写进报告的“测试分析”里反而是加分项。第三个验证动作是给关键代码加跟踪输出。如果不想用 IDE 调试器就在源码里加一个调试宏#ifdef ENABLE_TRACE #define TRACE(fmt, ...) printf([TRACE] fmt, __VA_ARGS__) #else #define TRACE(fmt, ...) #endif在调度循环里加一行 TRACE(pid%d state%d used%d\n, pcb_table[i].pid, pcb_table[i].state, pcb_table[i].used_time);编译时加 -DENABLE_TRACE 就能看到每个进程在每个时间片的完整状态路径。可以看到具体流程比盯着 exe 的输出日志猜逻辑更可靠。从那以后我每次拿到课程设计资源都强制自己先备份原版 exe再最小复现一次最后才开始改代码。你不用完全照搬但这个流程至少能让实验“血泪经验”转化成实打实的答辩素材希望帮到你。本文还有配套的精品资源点击获取