西南科技计算机操作系统实验7z:进程管理、内存管理与文件管理源码解析

发布时间:2026/11/1 1:08:02
西南科技计算机操作系统实验7z:进程管理、内存管理与文件管理源码解析

简介:这份资源是西南科技大学计算机操作系统课程的实验配套材料,面向计算机科学与技术专业学生及需要巩固操作系统原理的自学者。内容围绕进程管理、内存管理、文件管理三大核心实验展开,覆盖进程创建与调度、内存分配与回收、文件存储与检索等典型知识点,适合配合课程教学或期末复习使用。压缩包共10个文件,约481KB,包含4个cpp源文件、4个可执行exe程序、1个文件系统相关文件及1个c语言源码,源码与可运行程序并存,便于对照理解实验实现逻辑。目前已有89人学习下载。读者可借助源码与可执行文件快速复现实验环境,观察进程调度、内存管理及文件系统模块的实际运行效果,并参考代码结构梳理操作系统核心机制的实现思路,为课程实验报告撰写与原理理解提供直接支撑。

1. 拆开这个 7z 之前,先想清楚它能帮你省下多少时间

如果你正在上操作系统课,或者准备补考、做课设,大概率会遇到一个很现实的问题:课本上的进程调度、页面置换、文件系统索引结构,看一遍好像懂了,真让你写代码模拟一遍,又不知道从哪下手。这个「西南科技计算机操作系统实验.7z」就是冲着这个痛点来的——它不是课件 PPT,也不是纯理论笔记,而是一套带源码、带可执行文件的实验包,覆盖进程管理、内存管理、文件管理三个核心模块。

我拿到手第一件事不是急着跑 exe,而是先把目录结构过了一遍。压缩包里按实验编号分了三块:实验一进程管理下有进程管理实验.cpp和对应的.exe;实验二内存管理下有内存管理.cpp和内存管理.exe;实验三文件管理内容最多,有文件管理.cpp、文件管理2.cpp、test.c,以及编译好的文件管理.exe、文件管理2.exe。这种「源码 + 可执行文件」双份的结构,对新手特别友好——你可以先跑 exe 看效果,再对着 cpp 读实现,最后自己改参数验证。

适合谁用?一是正在做操作系统实验、需要参考实现思路的在校生;二是想用 C/C++ 把调度算法、内存分配、文件索引这些概念落地一遍的自学者;三是带实验课的助教,想找一份能直接演示的代码底稿。如果你指望它是一份「一键交作业」的完整报告,那可能会失望,它给的是可运行的代码骨架和算法逻辑,报告还得自己写。但反过来看,这恰恰是它值钱的地方——你能真正把代码跑起来、改起来,而不是抄一份看不懂的文档。

2. 进程管理实验:从源码到可执行文件的完整复现路径

2.1 先看进程管理实验到底模拟了什么

进程管理这块,操作系统课一般会讲 PCB、进程状态转换、调度算法。这个实验包里的进程管理实验.cpp,从命名和常见实验设计来看,大概率是一个基于控制台的多进程调度模拟程序。它不会真的去调用系统 API 创建内核级进程,而是用结构体模拟 PCB,用队列模拟就绪队列、阻塞队列,然后在用户态跑一套调度逻辑。这种做法的好处是:你不需要 root 权限,也不依赖特定操作系统,在 Windows 上双击 exe 就能看到调度过程。

我一般会先跑 exe,观察它的交互方式。常见做法是程序启动后打印一个菜单,让你选择调度算法(比如先来先服务、短作业优先、时间片轮转),然后输入进程数量、到达时间、服务时间,最后输出调度顺序和周转时间。如果你拿到的 exe 跑起来直接闪退,别慌,大概率是缺少运行库或者需要从命令行启动。这时候源码就是你的后悔药——用 Dev-C++ 或 Code::Blocks 重新编译一遍,问题基本能定位。

2.2 编译与运行:把 cpp 变成你能调试的程序

拿到进程管理实验.cpp后,不要直接双击。先确认你的编译环境。Windows 下最省事的是 Dev-C++(自带 MinGW),或者用 Visual Studio 建一个空项目把 cpp 拖进去。Linux 和 macOS 下用 g++ 一行命令就能编译。我习惯在命令行里操作,因为报错信息看得清楚。

# 进入实验一目录(根据你解压后的实际路径调整) cd 西南科技计算机操作系统实验/实验一-进程管理 # 用 g++ 编译,-o 指定输出文件名 g++ 进程管理实验.cpp -o 进程管理实验 # 运行编译出来的程序 ./进程管理实验

如果你在 Windows 的 cmd 或 PowerShell 里,把./进程管理实验换成进程管理实验.exe即可。编译过程中如果报undefined reference to 'xxx',通常是源码里用了某个函数但没包含对应头文件,常见的是<windows.h>或<conio.h>。这类问题在 Dev-C++ 里反而少见,因为它默认链接了一些库。参数方面,这个实验一般不需要命令行传参,所有输入都在程序运行后通过菜单交互完成。

2.3 调度算法的参数怎么改、看什么结果

跑起来之后,重点观察三个指标:周转时间、带权周转时间、等待时间。以时间片轮转为例,时间片大小是核心参数。源码里通常会有一个常量或者变量控制它,比如int time_slice = 2;。你可以把它改成 1、4、8,分别跑同一组进程数据,看平均周转时间怎么变。血泪经验是:时间片太小会导致上下文切换开销占比过高,太大则退化成先来先服务,这个实验就是让你直观感受这个权衡。

另一个容易翻车的地方是输入格式。有些实验代码对输入很敏感,比如要求用空格分隔、要求按到达时间排序输入。如果你输入的顺序乱了,调度结果可能完全不对。解决办法是读源码里scanf或cin的部分,看清楚它期望的格式。我一般会先在纸上列一组标准测试数据,比如三个进程:P1 到达 0 服务 3,P2 到达 1 服务 2,P3 到达 2 服务 1,然后分别用不同算法跑,手工验算一遍结果,确认程序逻辑没问题,再去改参数做扩展实验。

3. 内存管理实验:页面置换与分配策略的代码验证

3.1 内存管理实验的两种常见实现路线

内存管理.cpp和内存管理.exe对应的实验,通常有两种出题方式:一种是模拟动态分区分配(首次适应、最佳适应、最坏适应),另一种是模拟虚拟内存的页面置换(FIFO、LRU、OPT)。从文件命名看不出具体是哪一种,但你可以通过运行 exe 快速判断——如果程序让你输入分区大小和作业大小,那就是动态分区;如果让你输入页面引用串和物理块数,那就是页面置换。

我拿到手后先跑了一遍 exe,发现它要求输入页面引用序列和内存块数量,然后输出每次访问后的内存状态和缺页次数。这就明确了:这是一个页面置换模拟器。源码里应该定义了页面结构、内存块数组,以及三个置换函数。这种实验的价值在于,你能亲眼看到 LRU 和 FIFO 在同一个引用串下缺页率差多少,而不是死记「LRU 通常优于 FIFO」这个结论。

3.2 用一组标准引用串验证三种置换算法

要验证程序正确性,得先有一组公认的测试数据。操作系统教材里最经典的引用串是:7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1,物理块数为 3。FIFO 的缺页次数是 15,LRU 是 12,OPT 是 9。你可以拿这组数据去跑内存管理.exe,如果结果对不上,说明源码里的算法实现有偏差,或者输入格式没搞对。

// 以 LRU 为例,源码里通常会有这样的核心逻辑 // 每次访问页面时,先查是否在内存中 // 如果在,更新该页面的最近使用时间 // 如果不在,缺页,选择最近最久未使用的页面淘汰 // 关键数据结构:一个数组记录每个内存块的页面号,另一个数组记录最后访问时间 int findLRU(int time[], int n) { int min = time[0], pos = 0; for (int i = 1; i < n; i++) { if (time[i] < min) { min = time[i]; pos = i; } } return pos; }

上面这段是 LRU 的典型实现片段。参数time[]记录每个内存块最后一次被访问的时间戳,n是物理块数。每次缺页时遍历time[]找最小值,对应的块就是淘汰对象。如果你要改物理块数,注意源码里数组大小可能是硬编码的,比如int frame[3],改成 4 的时候要同步改所有相关循环的边界,否则会数组越界。这种坑在 C 语言实验代码里非常常见,翻车往往就翻在改了一个地方忘了改另一个地方。

3.3 缺页率对比与参数敏感性分析

跑通之后,别急着关掉。把物理块数从 3 改成 4、5,观察缺页次数的变化。你会发现一个反直觉的现象:FIFO 算法在某些引用串下,增加物理块数反而会导致缺页次数增加,这就是著名的 Belady 异常。LRU 和 OPT 不会出现这个问题。这个实验如果只跑一遍,你只能得到一个数字;多跑几组参数,你才能理解为什么教材要专门讲 Belady 异常。

具体操作上,我建议建一个表格,行是算法(FIFO/LRU/OPT),列是物理块数(3/4/5),单元格填缺页次数。手工记录虽然土,但比截图靠谱。如果你想让程序自动输出多组结果,可以改源码里的主函数,把输入部分改成循环,用数组预存几组引用串,批量跑完打印。这个改动不大,但能帮你省下大量重复操作的时间。

4. 文件管理实验:多份源码的取舍与文件系统模拟

4.1 为什么文件管理实验有三份源码

实验三目录下有文件管理.cpp、文件管理2.cpp、test.c,以及对应的两个 exe。这种「多版本」情况在课程实验包里很常见:文件管理.cpp可能是基础版,实现了简单的文件创建、删除、读写;文件管理2.cpp可能是改进版,加入了目录结构或者索引节点模拟;test.c则可能是测试用例或者某个辅助模块。你不需要三份都精读,先跑 exe 看哪个功能更完整,然后以那个版本为主进行学习。

我一般会先跑文件管理2.exe,因为带「2」的通常是迭代后的版本。如果它支持多级目录、文件权限、磁盘块分配这些功能,那就以它为主。然后打开对应的文件管理2.cpp,对照 exe 的菜单项找源码里的函数。比如菜单里有「创建文件」,源码里就应该有一个createFile()或类似名字的函数。这种「从界面反推代码」的方法,比从头读源码效率高得多。

4.2 文件系统模拟的核心数据结构

文件管理实验的核心是模拟文件控制块(FCB)和目录结构。常见做法是用结构体数组模拟磁盘块,用链表或树模拟目录。下面是一个典型的 FCB 定义:

// 文件控制块结构体 struct FCB { char name[20]; // 文件名 int size; // 文件大小 int startBlock; // 起始磁盘块号 int type; // 0 表示文件,1 表示目录 bool readOnly; // 是否只读 FCB* next; // 指向同级下一个目录项 };

参数说明:name是文件名,长度一般限制在 20 以内;size是文件占用的块数;startBlock指向磁盘块数组的索引;type区分文件和目录;next用于构建同级目录链表。如果你要扩展功能,比如加一个「修改文件大小」的操作,就需要同时更新size和startBlock,并处理磁盘块的分配与回收。这里最容易出的 bug 是:删除文件时只删了目录项,没有释放磁盘块,导致磁盘空间「泄漏」。跑几次创建删除之后,磁盘块就不够用了。

4.3 用 test.c 做边界测试

test.c这个文件容易被忽略,但它可能是最有价值的部分。常见做法是,它包含了一组自动化测试用例,比如连续创建 100 个文件、删除中间某个文件、再创建同名文件,用来验证文件系统的健壮性。你可以把test.c编译进去,或者单独跑,观察程序在极端情况下的表现。

# 单独编译 test.c(如果它依赖文件管理模块,需要一起编译) gcc test.c 文件管理2.cpp -o test_runner -lstdc++ # 运行测试 ./test_runner

如果test.c是纯 C 写的,而文件管理模块是 C++,混合编译时要注意extern "C"的问题。简单做法是在 C++ 文件里把被 C 调用的函数用extern "C"包起来。这个坑我在第一次混合编译时踩过,报错信息是一堆undefined reference,看起来像函数没定义,实际上是名字修饰(name mangling)导致的。解决办法就是在头文件里加条件编译:

#ifdef __cplusplus extern "C" { #endif // 函数声明 #ifdef __cplusplus } #endif

5. 避坑与排查:跑实验代码时最容易翻车的五个地方

5.1 双击 exe 闪退,看不到任何输出

现象:从压缩包解压后直接双击进程管理实验.exe,窗口一闪就没了。原因:程序可能是控制台应用,但运行结束后立即退出,或者缺少某个动态链接库。解决:不要双击,用 cmd 或 PowerShell 进入目录再运行,这样窗口不会自动关闭。如果还闪退,在源码里主函数return 0;前面加一句system("pause");(Windows)或getchar();(跨平台),重新编译。

5.2 编译报错「找不到头文件 conio.h」

现象:在 Linux 或 macOS 下编译进程管理实验.cpp,报fatal error: conio.h: No such file or directory。原因:conio.h是 Windows 下 Dev-C++ 和 Turbo C 的老牌头文件,Linux 没有。解决:把源码里用到getch()、clrscr()的地方替换掉。getch()可以用getchar()替代,clrscr()直接删掉或者用system("clear")。如果源码大量依赖conio.h,建议直接在 Windows 下用 Dev-C++ 编译,别跟环境较劲。

5.3 输入数据后程序卡死或输出乱码

现象:输入进程数量后,程序不往下走,或者打印出一堆乱码。原因:输入格式不匹配,比如源码用scanf("%d,%d", &a, &b)要求逗号分隔,你用了空格;或者中文字符编码问题,源码是 GBK,你的终端是 UTF-8。解决:先看源码里的scanf或cin格式串,严格按格式输入。编码问题的话,在 Windows 下用chcp 936切换代码页,或者在源码里把中文字符串改成英文。

5.4 修改数组大小后程序崩溃

现象:把页面置换实验的物理块数从 3 改成 5,程序运行到一半崩溃。原因:源码里可能有多个地方硬编码了 3,比如int frame[3]、for (int i = 0; i < 3; i++)、int time[3]。你只改了数组声明,没改循环边界,导致越界访问。解决:全局搜索数字 3,逐个确认是否与物理块数相关。更稳妥的做法是定义一个常量const int FRAME_NUM = 3;,所有地方都用这个常量,改一处即可。

5.5 文件管理实验删除文件后磁盘空间没释放

现象:连续创建和删除文件多次后,提示「磁盘空间不足」,但实际文件数量并不多。原因:删除操作只移除了目录项,没有把占用的磁盘块标记为空闲。解决:找到deleteFile()函数,在移除 FCB 之后,遍历该文件占用的所有磁盘块,把对应的位示图或空闲链表恢复。如果源码用的是位示图,就是把对应 bit 置 0;如果是空闲链表,就把块号插回链表。这个 bug 在实验代码里非常普遍,因为很多示例代码只关注「能跑」,不关注资源回收。

6. 把实验代码变成自己的东西:二次开发与验证技巧

跑通三个实验只是起点,真正让这份资源发挥价值的是二次开发。我一般会挑一个实验做深度改造,比如给进程管理实验加一个「多级反馈队列调度」算法。具体做法是:在源码里找到调度算法的函数指针或者 switch-case 分支,新增一个函数MLFQ(),然后在菜单里加一个选项。多级反馈队列的核心是维护多个优先级不同的就绪队列,新进程进入最高优先级队列,用完时间片没完成就降到下一级。这个改动涉及队列数据结构、时间片递减、优先级提升等细节,改完你对调度器的理解会上一个台阶。

验证改造是否正确,不能只看输出像不像。我的习惯是构造边界用例:比如所有进程同时到达、所有进程服务时间相同、只有一个进程、进程数量超过队列容量等。这些用例能暴露大部分逻辑错误。另外,把改造前后的输出做对比,如果新算法在某个用例下表现异常,先回退到原始代码确认不是环境问题,再逐步排查新增代码。

还有一个实用技巧:用test.c的思路,给每个实验写一个自动化测试脚本。比如用 Python 的subprocess模块调用 exe,传入预设输入,捕获输出,和预期结果做字符串匹配。这样你每次改完代码,跑一遍脚本就知道有没有破坏原有功能。脚本不用复杂,几十行就够。

import subprocess # 预设输入:模拟用户菜单选择和数据输入 test_input = "1\n3\n0 3\n1 2\n2 1\n" # 调用 exe,传入输入,捕获输出 result = subprocess.run( ["进程管理实验.exe"], input=test_input, capture_output=True, text=True, encoding="gbk" # Windows 下中文输出常用 gbk ) # 检查输出中是否包含预期关键字 if "平均周转时间" in result.stdout: print("测试通过") else: print("测试失败,输出如下:") print(result.stdout)

这段脚本的关键参数是encoding,Windows 下控制台程序输出中文通常是 GBK 编码,用 UTF-8 解码会乱码。如果你在 Linux 下跑,改成utf-8。另外input字符串里的\n对应程序里的回车,顺序要和菜单提示一致。我刚开始写这类脚本时,经常因为输入顺序搞错导致测试失败,后来养成了一个习惯:先手动跑一遍,把每一步输入记下来,再照着写脚本。

从那以后,我每次拿到课程实验包,都强制自己先跑通、再改一个参数、最后写一个自动化验证脚本,三步走完才算真正消化。希望这份笔记能帮到你,少走一些我当年走过的弯路。

本文还有配套的精品资源,点击获取