《操作系统实验》课程实验报告
目录
实验一 进 程 调 度 3
1.实验目的: 3
2.实验内容: 3
3.设计实现: 4
4.实验结果 17
5. 实验过程中出现的问题及解决办法 19
实验二 存储管理 20
1.实验目的: 20
2.实验内容: 20
3.设计实现: 21
4.实验结果 23
5.实验过程中出现的问题及解决办法 26
实验三 进 程 调 度 27
1.实验目的: 27
2.实验内容: 27
3.设计实现: 30
4.实验结果 38
5. 参考资料 45
实验一 进 程 调 度
1.实验目的:
通过对进程调度算法的模拟,进一步理解进程的基本概念,加深对进程运行状态和
进程调度过程、调度算法的理解。
2.实验内容:
2.1预备知识
1、先来先服务(FCFS)调度算法
先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。当在作业调度中采用该算法时,每次调度都是从后备作业队列中选择一个或多个最先进入该队列的作业,将它们调入内存,为它们分配资源、创建进程,然后放入就绪队列。在进程调度中采用 FCFS 算法时,则每次调度是从就绪队列中选择一个最先进入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃处理机。
2、最短作业优先调度算法
最短作业优先( SJF)调度算法,它会优先选择运⾏时间最短的进程来运⾏,这有助于提⾼系统的吞吐量。这显然对⻓作业不利,很容易造成⼀种极端现象。⽐如,⼀个⻓作业在就绪队列等待运⾏,⽽这个就绪队列有⾮常多的短作业,那么就会使得⻓作业不断的往后推,周转时间变⻓,致使⻓作业⻓期不会被运⾏。
3、高响应比优先调度算法
高响应比优先(HRRN)调度算法主要是权衡了短作业和长作业。每次进⾏进程调度时,先计算「响应⽐优先级」,然后把「响应比优先级」最⾼的进程投⼊运⾏,「响应⽐优先级」的计算公式:
- 响应比=响应时间/预计执行时间
- 响应时间=等待时间+预计执行时间
- 所以响应比为:1+作业等待时间/预计执行时间
从上⾯的公式,可以发现:如果两个进程的「等待时间」相同时,「要求的服务时间」越短,「响应⽐」就越⾼,这样短作业的进程容易被选中运⾏;如果两个进程「要求的服务时间」相同时,「等待时间」越⻓,「响应⽐」就越⾼,这就兼顾到了⻓作业进程,因为进程的响应⽐可以随时间等待的增加⽽提⾼,当其等待时间⾜够⻓时,其响应⽐便可以升到很⾼,从⽽获得运⾏的机会;
4、时间片轮转调度算法
每个进程被分配⼀个时间段,称为时间⽚(Quantum),即允许该进程在该时间段中运⾏。如果时间⽚⽤完,进程还在运⾏,那么将会把此进程从 CPU 释放出来,并把 CPU 分配给另外⼀个进程;如果该进程在时间⽚结束前阻塞或结束,则 CPU ⽴即进⾏切换;另外,时间⽚的⻓度就是⼀个很关键的点:如果时间⽚设得太短会导致过多的进程上下⽂切换,降低了 CPU 效率;如果设得太⻓⼜可能引起对短作业进程的响应时间变⻓。
2.2实验环境
1. 硬件设备:PC 机一台
2. 软件环境:Windows 操作系统、Visual Studio 2022
3.设计实现:
1)流程图
2)详细设计
#include <iostream>
#define MaxNum 100 // 最大进程数
double AverageWT_FCFS = 0, AverageWT_SJF = 0; // 平均等待时间
double AverageTAT_FCFS = 0, AverageTAT_SJF = 0; // 平均周转时间
double AverageWTAT_FCFS = 0, AverageWTAT_SJF = 0; // 平均带权周转时间
double AverageWT_HRRN = 0.0, AverageWT_RR = 0.0; // 平均等待时间
double AverageTAT_HRRN = 0.0, AverageTAT_RR = 0.0; // 平均周转时间
double AverageWTAT_HRRN = 0.0, AverageWTAT_RR = 0.0;// 平均带权周转时间
double CurrentTime = 0.0; // 当前时间
int ProNum = 0; // 进程数量
int DisplayNum = 0; // 输出进程信息数量
int ProIndex = 0; // 已完成的进程数量
double TimeSlice = 0; // RR算法的时间片
int RRnum = 0; // RR算法的进程数量
using namespace std;
// 进程结构体
typedef struct PROCESS
{
int index; // 进程序号
int Signal; // 第一次运行标志位 0表示该进程第一次运行 否则为1
int ProStatus; // 进程状态 0表示等待 1表示运行 2表示完成
double RunedTime; // RR算法已服务的时间
double ServiceTime; // 服务时间
double ArrivalTime; // 到达时间
double StartTime; // 开始时间
double FinishTime; // 结束时间
double TurnArroundTime; // 周转时间
double WaitTime; // 等待时间
double ResponseRatio; // 响应比
double WeightTurnArroundTime; // 带权周转时间
} PRO[MaxNum];
// 输出各进程的到达时间和服务时间
void display_base(PRO PC, int n)
{
cout << endl;
cout << "进程名\t"
<< "到达时间\t"
<< "服务时间" << endl;
for (int t = 0; t < n; t++)
cout << PC[t].index << "\t" << PC[t].ArrivalTime << "\t\t" << PC[t].ServiceTime << endl;
}
// 模拟整个调度过程,输出每个时刻的进程运行状态
void display_status(PRO PC, int n)
{
cout << endl;
cout << "进程名\t"
<< "开始时间\t"
<< "结束时间\t"
<< "就绪队列" << endl;
for (int t = 0; t < n; t++) // 循环输出每个进程的服务时间内 处在等待队列的进程 即到达时间在当前进程开始和结束时间之间的进程
{
cout << PC[t].index << "\t" << PC[t].StartTime << "\t\t" << PC[t].FinishTime << "\t\t";
for (int q = t + 1; q < n; q++)
{
if (PC[q].ArrivalTime <= PC[t].FinishTime)
cout << PC[q].index << " ";
}
cout << endl;
}
}
// 输出各进程的周转时间、带权周转时间和等待时间
void display_time(PRO PC, int n)
{
cout << endl;
cout << "进程名\t"
<< "周转时间\t"
<< "带权周转时间\t"
<< "等待时间" << endl;
for (int t = 0; t < n; t++)
cout << PC[t].index << "\t" << PC[t].TurnArroundTime << "\t\t" << PC[t].WeightTurnArroundTime << "\t\t" << PC[t].WaitTime << endl;
}
// 输出RR各进程的周转时间、带权周转时间和等待时间
void RRdisplay_time(PRO PC, int n)
{
cout << endl;
cout << "进程名\t"
<< "周转时间\t"
<< "带权周转时间\t"
<< "等待时间" << endl;
for (int t = 0; t < n; t++)
cout << PC[t].index << "\t" << PC[t].TurnArroundTime / 10000 << "\t\t" << PC[t].WeightTurnArroundTime << "\t\t" << PC[t].WaitTime / 10000 << endl;
}
// 输出所有进程的的平均周转时间、带权平均周转时间和平均等待时间
void display_average()
{
cout << endl;
cout << "算法名\t"
<< "平均周转时间\t"
<< "带权平均周转时间\t"
<< "平均等待时间" << endl;
cout << "FCFS\t" << AverageTAT_FCFS << "\t\t" << AverageWTAT_FCFS << "\t\t\t" << AverageWT_FCFS << endl;
cout << "SJF\t" << AverageTAT_SJF << "\t\t" << AverageWTAT_SJF << "\t\t\t" << AverageWT_SJF << endl;
cout << "HRRN\t" << AverageTAT_HRRN << "\t\t" << AverageWTAT_HRRN << "\t\t\t" << AverageWT_HRRN << endl;
cout << "RR\t" << AverageTAT_RR / 10000 << "\t\t" << AverageWTAT_RR << "\t\t\t" << AverageWT_RR / 10000 << endl;
}
// 按到达时间排序
void SortArrival(PRO& PC, int n)
{
PROCESS temp;
for (int i = 0; i < n; i++)
{
int min = i;
for (int j = i + 1; j < n; j++)
if (PC[j].ArrivalTime < PC[min].ArrivalTime)
min = j;
temp = PC[i];
PC[i] = PC[min];
PC[min] = temp;
}
}
// SJF按到达时间排序
void SortArrivalSJF(PRO& PC, int n)
{
PROCESS temp;
for (int i = 0; i < n; i++)
{
int min = i;
for (int j = i + 1; j < n; j++)
if (PC[j].ArrivalTime < PC[min].ArrivalTime)
min = j;
temp = PC[i];
PC[i] = PC[min];
PC[min] = temp;
}
int tmin = 0;
int End = PC[0].ArrivalTime;
for (int x = 0; x < n; x++)
{
for (int y = x + 1; y < n; y++)
{
if (PC[y].ArrivalTime <= End && PC[y].ServiceTime < PC[tmin].ServiceTime)
tmin = y;
}
// 将当前进程与等待队列中服务时间最短的进程进行交换
temp = PC[x];
PC[x] = PC[tmin];
PC[tmin] = temp;
End += PC[x].ServiceTime;
tmin = x + 1; // 重置tmin为当前等待队列中的首位索引
}
//上面的是改正后的代码,所有进程都会比较到达时间,如果到达时间相同则会比较服务时间
//// 类比选择排序的思想 从第二个到达的进程开始 遍历当前进程后面的进程 如果有服务时间比当前最短服务时间更短的 则交换位置
//int End = PC[0].ArrivalTime; // 之前服务的结束时间
//int tmin = 0; // 当前最短服务时间的索引
//PROCESS temp;
//for (int x = 0; x < n; x++)
//{
// for (int y = x + 1; y < n; y++)
// {
// if (PC[y].ArrivalTime <= End && PC[y].ServiceTime < PC[tmin].ServiceTime)
// tmin = y;
// }
// // 将当前进程与等待队列中服务时间最短的进程进行交换
// temp = PC[x];
// PC[x] = PC[tmin];
// PC[tmin] = temp;
// End += PC[x].ServiceTime;
// tmin = x + 1; // 重置tmin为当前等待队列中的首位索引
//}
}
// 计算各项时间 z为记号变量 用于区别FCFS、SJF和HRRN
void CountTime(PRO& PC, int n, int z)
{
PC[0].StartTime = PC[0].ArrivalTime;
PC[0].FinishTime = PC[0].ArrivalTime + PC[0].ServiceTime;
PC[0].TurnArroundTime = PC[0].FinishTime - PC[0].ArrivalTime;
PC[0].WaitTime = 0;
PC[0].WeightTurnArroundTime = PC[0].TurnArroundTime / PC[0].ServiceTime;
double sumWT = PC[0].WaitTime, sumTAT = PC[0].TurnArroundTime, sumWTAT = PC[0].WeightTurnArroundTime;
for (int m = 1; m < n; m++)
{
if (PC[m].ArrivalTime >= PC[m - 1].FinishTime)
{
PC[m].StartTime = PC[m].ArrivalTime;
PC[m].WaitTime = 0;
}
else
{
PC[m].StartTime = PC[m - 1].FinishTime;
PC[m].WaitTime = PC[m - 1].FinishTime - PC[m].ArrivalTime;
}
PC[m].FinishTime = PC[m].StartTime + PC[m].ServiceTime;
PC[m].TurnArroundTime = PC[m].FinishTime - PC[m].ArrivalTime;
PC[m].WeightTurnArroundTime = PC[m].TurnArroundTime / PC[m].ServiceTime;
sumWT += PC[m].WaitTime;
sumTAT += PC[m].TurnArroundTime;
sumWTAT += PC[m].WeightTurnArroundTime;
}
if (z == 1) // 计算FCFS
{
AverageWT_FCFS = sumWT / n;
AverageTAT_FCFS = sumTAT / n;
AverageWTAT_FCFS = sumWTAT / n;
}
if (z == 2) // 计算SJF
{
AverageWT_SJF = sumWT / n;
AverageTAT_SJF = sumTAT / n;
AverageWTAT_SJF = sumWTAT / n;
}
if (z == 3) // 计算HRRN
{
AverageWT_HRRN = sumWT / n;
AverageTAT_HRRN = sumTAT / n;
AverageWTAT_HRRN = sumWTAT / n;
}
}
// FCFS算法
void FCFS(PRO& PC, int n)
{
SortArrival(PC, n); // 按到达时间进行排序
CountTime(PC, n, 1); // 计算各项时间
display_status(PC, n); // 模拟整个调度过程,输出每个时刻的进程运行状态
display_time(PC, n); // 输出各进程的周转时间、带权周转时间和等待时间
}
// SJF算法
void SJF(PRO& PC, int n)
{
SortArrivalSJF(PC, n); // 先按到达时间进行排序
//* 这里是错误的代码,如果前两个都是同时到达,而第一个进程的服务时间比第二个进程的服务时间长,那么就会出现错误。
//// 类比选择排序的思想 从第二个到达的进程开始 遍历当前进程后面的进程 如果有服务时间比当前最短服务时间更短的 则交换位置
//int End = PC[0].ArrivalTime; // + PC[0].ServiceTime; // 之前服务的结束时间
//int tmin = 1; // 当前最短服务时间的索引
//PROCESS temp;
//for (int x = 1; x < n; x++)
//{
// for (int y = x + 1; y < n; y++)
// {
// if (PC[y].ArrivalTime <= End && PC[y].ServiceTime < PC[tmin].ServiceTime)
// tmin = y;
// }
// // 将当前进程与等待队列中服务时间最短的进程进行交换
// temp = PC[x];
// PC[x] = PC[tmin];
// PC[tmin] = temp;
// End += PC[x].ServiceTime;
// tmin = x + 1; // 重置tmin为当前等待队列中的首位索引
//}
CountTime(PC, n, 2); // 计算各项时间
display_status(PC, n); // 模拟整个调度过程,输出每个时刻的进程运行状态
display_time(PC, n); // 输出各进程的周转时间、带权周转时间和等待时间
}
// HRRN算法
void HRRN(PRO& PC, int n)
{
SortArrival(PC, n); // 先按到达时间排序
// 类比选择排序的思想 从第二个到达的进程开始 遍历当前进程后面的进程 如果有响应比比当前最短响应比更高的 则交换位置
int End = PC[0].ArrivalTime; // 之前服务的结束时间
int rmax = 0; // 当前最高响应比的索引
PROCESS temp;
for (int x = 0; x < n; x++)
{
for (int r = x; r < n; r++)
{
if (PC[r].ArrivalTime <= End)
PC[r].ResponseRatio = (End - PC[r].ArrivalTime + PC[r].ServiceTime) / PC[r].ServiceTime;
}
for (int y = x + 1; y < n; y++)
{
if ((PC[y].ArrivalTime <= End) && (PC[y].ResponseRatio >= PC[rmax].ResponseRatio))
//如果时间片相等
if (PC[y].ResponseRatio == PC[rmax].ResponseRatio)
{
//则比较服务时间,服务时间短的优先
if (PC[y].ServiceTime < PC[rmax].ServiceTime)
{
rmax = y;
}
}
else{
rmax = y;
}
}
// 将当前进程与等待队列中响应比最大的进程进行交换
temp = PC[x];
PC[x] = PC[rmax];
PC[rmax] = temp;
End += PC[x].ServiceTime;
rmax = x + 1; // 重置rmax为当前等待队列中的首位
}
CountTime(PC, n, 3); // 计算各项时间
display_status(PC, n); // 模拟整个调度过程,输出每个时刻的进程运行状态
display_time(PC, n); // 输出各进程的周转时间、带权周转时间和等待时间
}
// 改变就绪队列的顺序 将当前进程移动到队尾
void MovePro(PRO& PC, int index)
{
RRnum = 0;
for (int s = 0; s < ProNum; s++) // 更新等待队列的进程数量
{
if (PC[s].ArrivalTime <= CurrentTime && PC[s].ProStatus != 2)
RRnum++;
}
//下一个到达时间 = 当前时间
PROCESS temp = PC[index];
for (int i = index; i < RRnum - 1; i++)
{
PC[i] = PC[i + 1];
}
PC[RRnum - 1] = temp;
if(RRnum > 2)
{
int s = 0;
for (int i = 0; i < RRnum - 1; i++)
{
//如果当前进程的标志位为0,
//则把当前进程移动到循环队列的队尾,是指参与循环的最后位
if (PC[i].Signal == 0)
{
int b = i;
while (true)
{
//则把当前进程移动到循环队列的队尾,是指参与循环的最后位
//这里的if就是判断如果PC[b]前一个信号是0,说明PC[b]位置是循环队列的队尾
//或者已经来到了循环队列的队首,那么就不需要再移动了
if (PC[b - 1].Signal == 0 || b == 0)
{
break;
}
else
//如果PC[b]前一个信号是1,说明PC[b]位置不是循环队列的队尾
//那么就把PC[b]和PC[b-1]交换位置,然后b--,继续判断PC[b]前一个是不是
{
temp = PC[b];
PC[b] = PC[b - 1];
PC[b - 1] = temp;
b--;
}
}
}
else
{
s++;
}
}
//如果所有进程都已经运行过一次,则把所有进程的标志位都置为0
if (s == RRnum)
{
for (int j = 0; j < RRnum - 1; j++)
{
PC[j].Signal = 0;
}
}
}
}
// 删除已完成的进程
void RemovePro(PRO& PC, int index)
{
PC[index].ProStatus = 2;
//把已完成的进程移动到队尾
PROCESS temp = PC[index];
for (int i = index; i < ProNum - 1; i++)
{
PC[i] = PC[i + 1];
}
PC[ProNum - 1] = temp;
if (ProNum == 1) // 服务全部结束
{
cout << endl
<< "轮转完成 : " << endl;
}
ProIndex++;
ProNum--;
}
//进程完成并计算时间
int IfProEnd(PRO& PC, int index)
{
PC[index].FinishTime = CurrentTime;
PC[index].TurnArroundTime = PC[index].FinishTime - PC[index].ArrivalTime;
PC[index].WeightTurnArroundTime = PC[index].TurnArroundTime / PC[index].ServiceTime;
RemovePro(PC, index);
return 0;
}
// 指定时间片进程运行
int RunProcess(PRO& PC, int index)
{
//如果RR算法服务时间>时间片
if (PC[index].RunedTime > TimeSlice)
{
CurrentTime += TimeSlice;
if (PC[index].Signal == 0)
{
PC[index].StartTime = CurrentTime - TimeSlice;
PC[index].Signal = 1;
}
PC[index].ProStatus = 1;
PC[index].RunedTime -= TimeSlice;
MovePro(PC, index);
}
//如果RR算法服务时间<=时间片
else
{
//如果进程没有服务完就服务
if (PC[index].RunedTime != 0)
{
CurrentTime += PC[index].RunedTime;
if (PC[index].Signal == 0)
{
PC[index].StartTime = CurrentTime - PC[index].RunedTime;
PC[index].Signal = 1;
}
PC[index].ProStatus = 1;
PC[index].RunedTime = 0;
IfProEnd(PC, index);
}/*if (PC[index].ProStatus != 2)
if (PC[index].Signal == 0)
{
PC[index].StartTime = CurrentTime;
PC[index].Signal = 1;
}
//当前时间跳到进程服务完的时间
CurrentTime += PC[index].RunedTime;
PC[index].ProStatus = 1;
IfProEnd(PC, index);
return 0;*/
}
return 0;
}
// RR算法
void RR(PRO& PC, int n)
{
// 对全局变量进行初始化以便实现循环多次使用
CurrentTime = 0;
ProNum = n;
ProIndex = 0;
TimeSlice = 0;
for (int s = 0; s < n; s++) // 标志位初始化为0
{
PC[s].Signal = 0;
PC[s].ProStatus = 0;
//把服务时间给RR算法服务时间
PC[s].RunedTime = PC[s].ServiceTime * 10000;
//都*10000是为了防止出现小数
PC[s].ServiceTime = PC[s].ServiceTime * 10000;
PC[s].ArrivalTime = PC[s].ArrivalTime * 10000;
}
cout << "时间片大小 = ";
cin >> TimeSlice;
TimeSlice = TimeSlice * 10000;
SortArrival(PC, n); // 先按到达时间排序
cout << endl;
while (1)
{
if (ProIndex == n)
break; // 已完成的进程数量等于总数量 则退出循环
if (CurrentTime >= PC[0].ArrivalTime)
{
RunProcess(PC, 0);
}
else
{
//当前时间往后调到P[0].ArrivalTime
CurrentTime = PC[0].ArrivalTime;
}
}
SortArrival(PC, n); // 按到达时间排序 使输出的结果更方便观看
for (int i = 0; i < n; i++)
{
PC[i].FinishTime = PC[i].ArrivalTime + PC[i].TurnArroundTime;
PC[i].WaitTime = PC[i].TurnArroundTime - PC[i].ServiceTime;
}
cout << endl
<< "进程名\t"
<< "开始时间\t"
<< "结束时间"
<< endl;
for (int j = 0; j < n; j++)
{
cout << PC[j].index << "\t" << PC[j].StartTime / 10000 << "\t\t" << PC[j].FinishTime / 10000 << endl;
}
RRdisplay_time(PC, DisplayNum); // 输出各进程的周转时间、带权周转时间和等待时间
// 计算平均时间
double sumWT = PC[0].WaitTime, sumTAT = PC[0].TurnArroundTime, sumWTAT = PC[0].WeightTurnArroundTime;
for (int m = 1; m < n; m++)
{
sumWT += PC[m].WaitTime;
sumTAT += PC[m].TurnArroundTime;
sumWTAT += PC[m].WeightTurnArroundTime;
}
AverageWT_RR = sumWT / n;
AverageTAT_RR = sumTAT / n;
AverageWTAT_RR = sumWTAT / n;
}
int main()
{
PRO pc;
int n;
cout << "请输入进程的数量(最大值为100):";
cin >> n;
DisplayNum = n;
ProNum = n;
cout << "请输入每个进程的到达时间和服务时间:" << endl;
for (int i = 0; i < n; i++)
{
pc[i].index = i + 1;
cin >> pc[i].ArrivalTime >> pc[i].ServiceTime;
}
display_base(pc, n);
int choice;
cout << endl
<< "输入0-输出平均时间并退出程序 输入1-FCFS 输入2-SJF 输入3-HRRN 输入4-RR" << endl
<< "请选择要执行的操作:";
cin >> choice;
while (choice != 0)
{
if (choice == 1)
{
FCFS(pc, n);
//break;
}
if (choice == 2)
{ SJF(pc, n);
//break;
}
if (choice == 3)
{
HRRN(pc, n);
//break;
}
if (choice == 4)
{
RR(pc, n);
break;
}
cout << endl
<< "请输入接下来要执行的操作:输入0-输出平均时间并退出程序 输入1-FCFS 输入2-SJF 输入3-HRRN 输入4-RR" << endl
<< "请选择要执行的操作:";
cin >> choice;
}
display_average();
cout << endl;
system("pause");
return 0;
}
//ppt测试数据
/*
8 2
8.5 0.5
9 0.1
9.5 0.2
*/
//fcfs 平均周转时间:T=1.725平均带权周转时间W=6.875
//sjf 平均周转时间:T=1.55平均带权周转时间W=5.15
//hrrn 平均周转时间:T=1.625平均带权周转时间W=5.675
/*
0 4
1 3
2 4
3 2
4 4
*/
//RR
//时间片=1 平均周转时间:T=11.8平均带权周转时间W=3.43
//时间片=4 平均周转时间:T=8.4平均带权周转时间W=2.7
以下代码为另一种RR的设计,区别在于新到的进程应该放在哪里
#include <iostream>
#define MaxNum 100 // 最大进程数
double CurrentTime = 0.0; // 当前时间
double AverageWT_HRRN = 0.0, AverageWT_RR = 0.0; // 平均等待时间
double AverageTAT_HRRN = 0.0, AverageTAT_RR = 0.0; // 平均周转时间
double AverageWTAT_HRRN = 0.0, AverageWTAT_RR = 0.0; // 平均带权周转时间
int ProNum = 0; // 进程数量
int DisplayNum = 0; // 输出进程信息数量
int ProIndex = 0; // 已完成的进程数量
int TimeSlice = 0; // 时间片
using namespace std;
// 进程结构体
typedef struct PROCESS
{
int index; // 进程序号
int RunedTime; // RR算法已服务的时间
int Signal; // 第一次运行标志位 0表示该进程第一次运行 否则为1
int ProStatus; // 进程状态 0表示等待 1表示运行 2表示完成
double ServiceTime; // 服务时间
double ArrivalTime; // 到达时间
double StartTime; // 开始时间
double FinishTime; // 结束时间
double TurnArroundTime; // 周转时间
double WaitTime; // 等待时间
double ResponseRatio; // 响应比
double WeightTurnArroundTime; // 带权周转时间
} PRO[MaxNum];
// 输出各进程的到达时间和服务时间
void display_base(PRO PC, int n)
{
cout << endl;
cout << "Process Num\t"
<< "Arrival Time\t"
<< "ServiceTime" << endl;
for (int t = 0; t < n; t++)
cout << PC[t].index << "\t\t" << PC[t].ArrivalTime << "\t\t" << PC[t].ServiceTime << endl;
}
// 模拟整个调度过程 输出每个时刻的进程运行状态
void display_status(PRO PC, int n)
{
cout << endl;
cout << "Process Num\t"
<< "Start Time\t"
<< "End Time\t"
<< "就绪队列" << endl;
for (int t = 0; t < n; t++) // 循环输出每个进程的服务时间内 处在等待队列的进程 即到达时间在当前进程开始和结束时间之间的进程
{
cout << PC[t].index << "\t\t" << PC[t].StartTime << "\t\t" << PC[t].FinishTime << "\t\t";
for (int q = t + 1; q < n; q++)
{
if (PC[q].ArrivalTime <= PC[t].FinishTime)
cout << PC[q].index << " ";
}
cout << endl;
}
}
// 输出各进程的周转时间、带权周转时间和等待时间
void display_time(PRO PC, int n)
{
cout << endl;
cout << "Process Num\t"
<< "周转时间\t"
<< "带权周转时间\t"
<< "等待时间" << endl;
for (int t = 0; t < n; t++)
cout << PC[t].index << "\t\t" << PC[t].TurnArroundTime << "\t\t" << PC[t].WeightTurnArroundTime << "\t\t" << PC[t].WaitTime << endl;
}
// 输出所有进程的的平均周转时间、带权平均周转时间和平均等待时间
void display_average()
{
cout << endl;
cout << "类别\t\t"
<< "平均周转时间\t"
<< "带权平均周转时间\t"
<< "平均等待时间" << endl;
cout << "RR\t\t" << AverageTAT_RR << "\t\t" << AverageWTAT_RR << "\t\t" << AverageWT_RR << endl;
}
// 按到达时间排序
void SortArrival(PRO& PC, int n)
{
PROCESS temp;
for (int i = 0; i < n; i++)
{
int min = i;
for (int j = i + 1; j < n; j++)
if (PC[j].ArrivalTime < PC[min].ArrivalTime)
min = j;
temp = PC[i];
PC[i] = PC[min];
PC[min] = temp;
}
}
// RR输出函数
void PrintRR(PRO PC)
{
cout << endl
<< "当前时间 : " << CurrentTime << endl;
cout << "Process Num\t"
<< "Start Time\t"
<< "轮转次数\t"
<< "进程状态\t" << endl;
for (int t = 0; t < DisplayNum; t++) // 循环输出每个进程的服务时间内 处在等待队列的进程 即到达时间在当前进程开始和结束时间之间的进程
cout << PC[t].index << "\t\t" << PC[t].StartTime << "\t\t" << PC[t].RunedTime << "\t\t" << PC[t].ProStatus << endl;
}
// 改变就绪队列的顺序 将当前进程移动到队尾
void MovePro(PRO& PC, int index)
{
int Num = 0;
for (int s = 0; s < ProNum; s++) // 更新等待队列的进程数量
if (PC[s].ArrivalTime <= CurrentTime)
Num++;
PROCESS temp = PC[index];
for (int i = index; i < Num - 1; i++)
{
PC[i] = PC[i + 1];
}
PC[Num - 1] = temp;
}
// 删除已完成的进程
void RemovePro(PRO& PC, int index)
{
MovePro(PC, index);
PC[ProNum - 1].ProStatus = 2;
if (ProNum == 1) // 服务全部结束
{
cout << endl
<< "Finally Result : " << endl;
PrintRR(PC);
}
ProIndex++;
ProNum--;
}
// 判断一个进程是否完成并计算时间
int IfProEnd(PRO& PC, int index)
{
if (PC[index].ServiceTime == PC[index].RunedTime)
{
PC[index].FinishTime = CurrentTime;
PC[index].TurnArroundTime = PC[index].FinishTime - PC[index].ArrivalTime;
PC[index].WeightTurnArroundTime = PC[index].TurnArroundTime / PC[index].ServiceTime;
RemovePro(PC, index);
return 1;
}
return -1;
}
// 指定时间片进程运行
int RunProcess(PRO& PC, int index)
{
for (int i = 0; i < TimeSlice; i++) // 每个单位时间循环一次
{
CurrentTime++;
if (PC[index].Signal == 0)
{
PC[index].StartTime = CurrentTime - 1;
PC[index].Signal = 1;
}
PC[index].ProStatus = 1;
PC[index].RunedTime++;
PrintRR(PC); // 每个单位时间输出一次结果
if (IfProEnd(PC, index) == 1)
return 0;
}
MovePro(PC, index);
PC[ProNum - 1].ProStatus = 0;
return 0;
}
// RR算法
void RR(PRO& PC, int n)
{
// 对全局变量进行初始化以便实现循环多次使用
CurrentTime = 0;
ProNum = n;
ProIndex = 0;
TimeSlice = 0;
for (int s = 0; s < n; s++) // 标志位初始化为0
{
PC[s].Signal = 0;
PC[s].ProStatus = 0;
PC[s].RunedTime = 0;
}
cout << "TimeSlice = ";
cin >> TimeSlice;
SortArrival(PC, n); // 先按到达时间排序
cout << endl;
while (1)
{
if (CurrentTime >= PC[0].ArrivalTime)
{
RunProcess(PC, 0);
}
else
{
MovePro(PC, 0);
PC[ProNum - 1].ProStatus = 0;
}
if (ProIndex == n)
break; // 已完成的进程数量等于总数量 则退出循环
}
SortArrival(PC, n); // 按到达时间排序 使输出的结果更方便观看
for (int i = 0; i < n; i++)
{
PC[i].FinishTime = PC[i].ArrivalTime + PC[i].TurnArroundTime;
PC[i].WaitTime = PC[i].TurnArroundTime - PC[i].ServiceTime;
}
cout << endl
<< "Process Num\t"
<< "Start Time\t"
<< "End Time"
<< endl;
for (int j = 0; j < n; j++)
{
cout << PC[j].index << "\t\t" << PC[j].StartTime << "\t\t" << PC[j].FinishTime << endl;
}
display_time(PC, DisplayNum); // 输出各进程的周转时间、带权周转时间和等待时间
// 计算平均时间
double sumWT = PC[0].WaitTime, sumTAT = PC[0].TurnArroundTime, sumWTAT = PC[0].WeightTurnArroundTime;
for (int m = 1; m < n; m++)
{
sumWT += PC[m].WaitTime;
sumTAT += PC[m].TurnArroundTime;
sumWTAT += PC[m].WeightTurnArroundTime;
}
AverageWT_RR = sumWT / n;
AverageTAT_RR = sumTAT / n;
AverageWTAT_RR = sumWTAT / n;
}
int main()
{
PRO pc;
int n;
cout << "请输入进程的数量:";
cin >> n;
DisplayNum = n;
ProNum = n;
cout << "请输入每个进程的到达时间和服务时间:" << endl;
for (int i = 0; i < n; i++)
{
pc[i].index = i + 1;
cin >> pc[i].ArrivalTime >> pc[i].ServiceTime;
}
display_base(pc, n);
RR(pc, n);
cout << endl;
display_average();
cout << endl;
//system("pause");
return 0;
}
4.实验结果
实验过程中出现的问题及解决办法
在短时作业优先算法SJF和响应比优先HRRN中,我一开始想把队列首个程序运行完然后判断在重新排序,这样会出现一个问题,比如第一个程序服务时间并不是最短或者响应比并不是最高,他本应放队尾。但是在我的程序中他被放到了第一个,所以我对代码有所修改,使得在一开始就判断整个到达队列,做出符合短作业优先和响应比优先的队列顺序。
在RR算法中我所纠结的点是,当整个队列时间片循环正好结束的时候,一个新程序的到来是应该算在此次循环中还是算再下一次循环的依始。所以我做了两个RR程序。一套符合前者一套符合后者。他们两个主要的构思区别是关于时间的流动,一个是线性的时间流动,时间片一片一片运行并汇报当时状况。一个是非线性的时间流动,在这个系统中时间是跳跃的,在这个程序中只有什么时间点程序开始,结束时间点是:如果服务时间大于时间片就是,开始时间加上时间片时间,并且这个程序放到队列末尾;如果服务时间小于时间片结束时间点就是开始时间加上服务时间,下一个程序开始时间点也就是这个程序的结束时间。
实验二 存储管理
1.实验目的:
1)通过编写程序实现请求分页存储管理的 FIFO、LRU 页面置换算法,掌握虚拟存储管理中有关缺页处理方法等内容,巩固有关虚拟存储管理的教学内容。
2)理解内存分配原理,特别是以页面为单位的虚拟内存分配方法。
2.实验内容:
2.1预备知识
关于操作系统的内存管理,如何节省利用容量不大的内存为最多的进程提供资源,一直是研究的重要方向。而内存的虚拟存储管理,是现在最通用,最成功的方式—— 在内存有限的情况下,扩展一部分外存作为虚拟内存,真正的内存只存储当前运行时所用得到信息。这无疑极大地扩充了内存的功能,极大地提高了计算机的并发度。虚拟页式存储管理,则是将进程所需空间划分为多个页面,内存中只存放当前所需页面,其余页面放入外存的管理方式。
置换算法有:最佳置换算法 OPT、FIFO 置换算法、最少使用页面置换算法、最近未使用页面置换算法、时钟页面置换算法等
OPT 算法是理论算法,它将不再使用的页面换出,而实际中不能预知哪个页面不再使用。这个算法是理论上的最优算法,可以作为评测其他算法的性能。
FIFO 算法:FIFO( First In First Out)简单说就是指先进先出,是内存管理的一种页面置换算法。按照页面装进内存的时间进行置换,老的页面最先被换出,不管该页面是否经常使用,这样就有可能导致缺页率增加,导致页面置换次数增加。
LRU 是 Least Recently Used 近期最少使用算法。该算法按照上次使用时间进行排序,将离上次使用时间最长的页面换出。可以采用栈的数据结构,每次页面被访问将该页面号放在栈顶。也可使用移位寄存器实现:设置引用位 R,每次调用将 R=1,系统每个一段时间将R=0,当进行置换式检查哪个页面为零说明近期不会再使用,可以将其换出。
2.2实验环境
1. 硬件设备:PC 机一台
2. 软件环境:Windows 操作系统、Visual Studio 2022
3.设计实现:
1)流程图
2)详细设计
#include <iostream>
using namespace std;
//遍历哪块中是当前页面,没有则返回-1
int isBlockContainPage(int* block, int bnum, int page) {
for (int i = 0; i < bnum; i++) {
if (block[i] == page) {
return i;
}
}
return -1;
}
//输出块中内容
void printBlockContent(int* block, int bnum) {
cout << "块中内容:" << endl;
for (int i = 0; i < bnum; i++) {
cout << "\t" << block[i];
}
cout << endl;
cout << endl;
}
void FIFO(int *page, int *block, int pnum, int bnum) {//先进先出页面置换算法
int count = 0;
int change = 0;
for (int i = 0; i < pnum; i++) {
int panduan = isBlockContainPage(block, bnum, page[i]);
if (panduan != -1) {
cout << "第" << i+1 << "页面,内容" << page[i] << "命中,命中在" << panduan + 1 << "号块" << endl;
count++;
}
else {
cout << "第" << i+1 << "页面,内容" << page[i] << "缺页,进行页面置换" << endl;
//缺页
block[change] = page[i];
change = (change + 1) % bnum;
}
printBlockContent(block, bnum);
}
//计算缺页率
cout << "缺页率:" << (pnum - count) * 1.0 / pnum << endl;
}
void LRU(int *page, int *block, int pnum, int bnum) {//最近最久未使用页面置换算法
int count = 0;
int* change = new int[bnum];
for (int i = 0; i < bnum; i++) {
change[i] = 0;
}
//delete[] change;
for (int i = 0; i < pnum; i++) {
int panduan = isBlockContainPage(block, bnum, page[i]);
if (panduan != -1) {
cout << "第" << i + 1 << "页面,内容" << page[i] << "命中,命中在" << panduan + 1 << "号块" << endl;
count++;
//change都加1
for (int j = 0; j < bnum; j++) {
change[j]++;
}
change[panduan] = 0;
}
else {
cout << "第" << i + 1 << "页面,内容" << page[i] << "缺页,进行页面置换" << endl;
//缺页
// 当页面数大于块数时,进行置换
if (i > bnum){
//change用来计算每个块的最近使用次数,最大的那个块进行置换
int max = 0;
int maxnum = 0;
for (int j = 0; j < bnum; j++) {
if (change[j] > max) {
max = change[j];
maxnum = j;
}
}
block[maxnum] = page[i];
for (int j = 0; j < bnum; j++) {
change[j]++;
}
change[maxnum] = 0;
}
else {
block[i] = page[i];
for (int j = 0; j < bnum; j++) {
change[j]++;
}
change[i] = 0;
}
}
printBlockContent(block, bnum);
}
//计算缺页率
cout << "缺页率:" << (pnum - count) * 1.0 / pnum << endl;
//delete[] change;
}
int main() {
int page[200], block[100], pnum, bnum;
cout << "请输入页面数目和块数目:";
cin >> pnum >> bnum;
cout << "请输入页面号:";
for (int i = 0; i < pnum; i++) {
cin >> page[i];
}
//cout << page[45];
for (int i = 0; i < bnum; i++) {
block[i] = -1;
}
//选1FIFO或2LRU
int choice;
cout << "请选择页面置换算法:1.FIFO 2.LRU" << endl;
cin >> choice;
if (choice == 1)
{
cout << "FIFO算法" << endl;
FIFO(page, block, pnum, bnum);
}
else if (choice == 2)
{
cout << "LRU算法" << endl;
LRU(page, block, pnum, bnum);
}
else
{
cout << "输入错误" << endl;
return 0;
}
return 0;
}
//ppt测试数据
// fifo
// 21个页面,3个块,缺页率0.714
//7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 1 7 0 1
// lru
// 20个页面,3个块,缺页率0.600
//7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1
4.实验结果
- FIFO
- LRU
5.实验过程中出现的问题及解决办法
此次实验的难点,在于如何确定要被替换块的位置。通过观察PPT最终用数组的形式记录信息,此次实验可以设置三个数组,第一个数组为记录页的顺序、第二个数组记录块所存的内容、第三个数组用于记录即将要被替换的块儿的位置。
观察先进先出FIFO算法,发现会被替换的位置和命中页无关,只和缺页时候有关,缺页的时候要被替换的位置是缺页的次数除以块的数量的余。change = (change + 1) % bnum;所以这行代码为FIFO算法的关键。
观察最近最久未使用LLU算法,发现被替换块的位置和命中时也有关命中时即被使用重新计算。是最久没有被打开的文件被替换。由此给出代码。
for (int j = 0; j < bnum; j++) {change[j]++; }
change[panduan] = 0;当它命中或替换后,其余的使用次数都加一,它的使用次数清零,下次被替换的就是change数组中最大的元素的位置。但这时会出现一个新的问题,在刚开始块中没有内容时,不知道要用哪个块,所以就加了个if语句,只有在页面数大于会时才开始执行这种规则。
实验三 进 程 调 度
1.实验目的:
编写一个简单的二级文件系统实现程序,加深对文件系统的内部功能和内部实现的理解。
2.实验内容:
2.1预备知识
最基本的文件操作
1)创建文件
在创建一个新文件时,系统首先要为新文件分配必要的外存空间,并在文件系统的目录中,为之建立一个目录项。目录项中应该记录新文件的文件名及其在外存的地址等属性。
2)删除文件
当已不再需要某文件时,可将它从文件系统中删除。在删除时,系统应该先从目录中找到要删除的文件的目录项,使之成为空项,然后回收该文件所占用的存储空间。
3)读文件
在读一个文件时,须在相应的系统调用中给出文件名和应该读入的内存目标地址。此时,系统同样要查找目录,找到制定的目录项,从中得到被读文件在外存中的位置。
在目录项中,还有一个指针用于对文件的读/写。
4)写文件
在写一个文件时,须在相应的系统调用中给出该文件名及该文件在内存中的(源)地址。为此,也同样须先查找目录,找到指定文件的目录项,在利用目录中的写指针进行写操作。
5)设置文件的读/写位置
前述的文件读/写操作,都只提供了对文件顺序存取的手段,即每次都是从文件的始端读或写。设置文件读/写位置的操作,用于设置文件读/写指针的位置,以便每次读/写文件时,不是从其始端而是从所设置的位置开始操作。也正因如此,才能改顺序存取为随机存取。
- 文件系统基础:包括文件概念、文件的逻辑结构(顺序文件,索引文件,索引顺序文件)、目录结构(文件控制块和索引结点,单级目录结构和两级目录结构,树形目录结构,图形目录结构)、文件共享和文件保护(访问类型,访问控制)。
- 文件系统实现:包括文件系统层次结构、目录实现、文件实现。
- 磁盘组织与管理:包括磁盘的结构、磁盘调度算法、磁盘的管理。
- 文件
文件是指由创建者所定义的一组相关信息的集合,逻辑上可分为有结构文件和无结构文件两种。在有结构文件中,文件由一组相似记录组成,如报考某学校的所有考生的报考信息记录,又称记录式文件;而无结构文件则被看成是一个字符流,比如一个二进制文件或字符文件,又称流式文件。
虽然上面给出了结构化的表述,但实际上关于文件并无严格的定义。通常在操作系统中将程序和数据组织成文件。文件可以是数字、字母或二进制代码,基本访问单元可以是字节、 行或记录。文件可以长期存储于硬盘或其他二级存储器中,允许可控制的进程间共享访问,能够被组织成复杂的结构。
文件有一定的属性,这根据系统的不同而有所不同,但是通常都包括如下属性:
①名称:文件名称唯一,以容易读取的形式保存。
②标识符:标识文件系统内文件的唯一标签,通常为数字,它是对人不可读的一种内部名称
③类型:被支持不同类型的文件系统所使用。
④位置:指向设备和设备上文件的指针。
⑤大小:文件当前大小(用字节、字或块表示),也可包含文件允许的最大值。
⑥保护:对文件进行保护的访问控制信息。
⑦时间、日期和用户标识:文件创建、上次修改和上次访问的相关信息,用于保护、 安全和跟踪文件的使用。
所有文件的信息都保存在目录结构中,而目录结构也保存在外存上。文件信息当需要时再调入内存。通常,目录条目包括文件名称及其唯一标识符,而标识符定位其他属性的信息
文件的基本橾作
①创建文件:创建文件有两个必要步骤,一是在文件系统中为文件找到空间;二是在目录中为新文件创建条目,该条目记录文件名称、在文件系统中的位置及其他可能信息。
②写文件:为了写文件,执行一个系统调用,指明文件名称和要写入文件的内容。对于给定文件名称,系统搜索目录以查找文件位置。系统必须为该文件维护一个写位置的指针。每当发生写操作,便更新写指针。
③读文件:为了读文件,执行一个系统调用,指明文件名称和要读入文件块的内存位置。同样,需要搜索目录以找到相关目录项,系统维护一个读位置的指针。每当发生读操作时,更新读指针。一个进程通常只对一个文件读或写,所以当前操作位置可作为每个进程当前文件位置指针。由于读和写操作都使用同一指针,节省了空间也降低了系统复杂度。
④删除文件:先从目录中找到要删除文件的目录项,使之成为空项,然后回收该文件所占用的存储空间。
文件的打开与关闭
每个打开文件都有如下关联信息:
文件指针:系统跟踪上次读写位置作为当前文件位置指针,这种指针对打开文件的某个进程来说是唯一的,因此必须与磁盘文件属性分开保存。
文件打开计数:文件关闭时,操作系统必须重用其打开文件表条目,否则表内空间会不够用。因为多个进程可能打开同一个文件,所以系统在删除打开文件条目之前,必须等待最后一个进程关闭文件。该计数器跟踪打开和关闭的数量,当该计数为0 时,系统关闭文件,删除该条目。
文件磁盘位置:绝大多数文件操作都要求系统修改文件数据。该信息保存在内存中以免为每个操作都从磁盘中读取。
文件的逻辑结构:有结构文件(记录式文件):索引文件。
如图所示。对于定长记录文件,如果要查找第i个记录,可直接根据下式计算来获得第i个记录相对于第一个记录的地址:
然而,对于可变长记录的文件,要查找第i个记录时,必须顺序地查找前i-1个记录,从而获得相应记录的长度L,然后才能按下式计算出第i个记录的首址:
注意:假定每个记录前用一个字节指明该记录的长度。
1) 文件控制块。
文件控制块(FCB)是用来存放控制文件需要的各种信息的数据结构,以实现“按名存取”。FCB的有序集合称为文件目录,一个FCB就是一个文件目录项。为了创建一个新文件,系统将分配一个FCB并存放在文件目录中,成为目录项。
FCB主要包含以下信息:
基本信息,如文件名、文件的物理位置、文件的逻辑结构、文件的物理结构等。存取控制信息,如文件存取权限等。使用信息,如文件建立时间、修改时间等。
2) 索引结点。
在检索目录文件的过程中,只用到了文件名,仅当找到一个目录项(查找文件名与目录项中文件名匹配)时,才需要从该目录项中读出该文件的物理地址。在检索目录时,文件的其他描述信息不会用到,也不需调入内存。因此,有的系统(如图所示)釆用了文件名和文件描述信息分开的方法,文件描述信息单独形成一个称为索引结点的数据结构,简称为 i 结点。在文件目录中的每个目录项仅由文件名和指向该文件所对应的i结点的指针构成。
表4-1 UNIX的文件目录结构 | |
文件名 | 索引结点编号 |
文件名1 | |
文件名2 |
两级目录结构。
单级目录很容易造成文件名称的混淆,可以考虑釆用两级方案,将文件目录分成主文件目录(Master File Directory, MFD)和用户文件目录(User File Directory, UFD)两级,如图所示。
主文件目录项记录用户名及相应用户文件目录所在的存储位置。用户文件目录项记录该用户文件的FCB信息。当某用户欲对其文件进行访问时,只需搜索该用户对应的UFD,这既解决了不同用户文件的“重名”问题,也在一定程度上保证了文件的安全。
两级目录结构可以解决多用户之间的文件重名问题,文件系统可以在目录上实现访问限制。但是两级目录结构缺乏灵活性,不能对文件分类。
2.2实验环境
1. 硬件设备:PC 机一台
2. 软件环境:Windows 操作系统、Visual Studio 2022
3.设计实现:
1)流程图
2)详细设计
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_NONSTDC_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<time.h>
using namespace std;
// 创建磁盘二维数组
char disk[50][50];
// 磁盘状态
// 0表示空闲,1表示已占用
int disk_status[50] = { 0 };
//初始化
void init_disk() {
for (int i = 0; i < 50; i++) {
for (int j = 0; j < 51; j++) {
disk[i][j] = '\0';
}
}
}
// 文件结构体
//文件目录分成主文件目录(Master File Directory, MFD)和用户文件目录(User File Directory, UFD)两级
//用户文件目录(User File Directory, UFD)
typedef struct UFD {
char* filename;
struct contentNode* contentHead;
bool flag = 0;
int size;
int length;
} file;
// 内容结点结构体UOF块
// 内容结点结构体
typedef struct contentNode {
char* content;
int disk_address;
int disk_blocknum;
struct contentNode* next;
} contentNode;
// 主文件目录(Master File Directory, MFD)
typedef struct MFD {
char* username;
char* userpwd;
file* files[100];
int num_files;
} user;
// 函数声明
void create_user(); // 创建用户
void delete_user(); // 删除用户
void display_users(); // 展示用户
int login_user(); // 登录用户
int count_files(); // 统计文件个数
void create_file(user* u);// 创建文件
void delete_file(user* u);// 删除文件
file* open_file(user* u);// 打开文件
void display_directory(user* u);// 显示目录
void display_file(user* u);// 显示文件
user* alter_user(user* u);// 修改用户信息
void close_file(file* f);// 关闭文件
void read_file(file* f);// 读取文件
void write_file(file* f);// 写入文件
void print_menu(); // 打印菜单
// 全局变量
user users[100];// 用户数组,存储用户信息
user* u;// 用户指针
int num_users = 0;// 当前用户数量为0
file* curFile;// 当前文件指针
int user_id;// 用户ID
int cur_disk_address = 0;//当前磁盘位置
int null_disk_block = 50;//询空闲快数
// 创建用户
void create_user() {
if (num_users >= 100) {
printf("用户数已达上限\n");
return;
}
char newname[100];
printf("请输入新用户名:");
fgets(newname, 100, stdin);
newname[strcspn(newname, "\n")] = 0;
for (int i = 0; i < num_users; i++) {
if (strcmp(users[i].username, newname) == 0) {
printf("用户名已存在\n");
return;
}
}
users[num_users].username = strdup(newname);
num_users++;
printf("用户创建成功\n");
}
// 展示用户
void display_users() {
printf("所有用户:\n");
for (int i = 0; i < num_users; i++) {
printf("%s\n", users[i].username);
}
}
// 登录用户
int login_user() {
display_users();
char username[100];
printf("请输入用户名:");
fgets(username, 100, stdin);
username[strcspn(username, "\n")] = 0;
int index = -1;
for (int i = 0; i < num_users; i++) {
if (strcmp(users[i].username, username) == 0) {
index = i;
break;
}
}
if (index == -1) {
printf("用户不存在\n");
return 0;
}
u = &users[index];
printf("用户 %s 登录成功\n", u->username);
return 1;
}
// 删除用户
void delete_user() {
char username[100];
printf("请输入要删除的用户名:");
fgets(username, 100, stdin);
username[strcspn(username, "\n")] = 0;
int index = -1;
for (int i = 0; i < num_users; i++) {
if (strcmp(users[i].username, username) == 0) {
index = i;
break;
}
}
if (index == -1) {
printf("用户不存在\n");
return;
}
//把该用户所有的文件全删除
for (int i = 0; i < users[index].num_files; i++)
{
contentNode* current = users[index].files[i]->contentHead;
contentNode* temp;
//该用户磁盘中的内容全删除
while (current != NULL) {
temp = current;
current = current->next;
free(temp->content);
free(temp);
}
free(users[index].files[i]->filename);
free(users[index].files[i]);
}
free(users[index].username);
for (int i = index; i < num_users - 1; i++) {
users[i] = users[i + 1];
}
num_users--;
printf("用户删除成功\n");
}
// 统计磁盘
int count_files() {
int count = 0;
for (int i = 0; i < num_users; i++) {
count += users[i].num_files;
}
//打印磁盘和磁盘状态
for (int i = 0; i < 50; i++) {
printf("%d", disk_status[i]);
}
printf("\n");
for (int i = 0; i < 1000; i++) {
for (int j = 0; j < 100; j++) {
printf("%c", disk[i][j]);
}
printf("\n");
}
return count;
}
// 创建文件
void create_file(user* u) {
char newname[100];
printf("请输入要创建的文件名:");
fgets(newname, 100, stdin);
newname[strcspn(newname, "\n")] = 0;
for (int i = 0; i < u->num_files; i++) {
if (strcmp(u->files[i]->filename, newname) == 0) {
printf("文件已存在\n");
return;
}
}
file* f = (file*)malloc(sizeof(file));
f->filename = strdup(newname);
f->contentHead = NULL;
f->flag = false;
f->size = 0;
u->files[u->num_files++] = f;
printf("文件创建成功\n");
}
// 删除文件
void delete_file(user* u) {
char filename[100];
printf("请输入要删除的文件名:");
fgets(filename, 100, stdin);
filename[strcspn(filename, "\n")] = 0;
int index = -1;
for (int i = 0; i < u->num_files; i++) {
if (strcmp(u->files[i]->filename, filename) == 0) {
index = i;
break;
}
}
if (index == -1) {
printf("文件不存在\n");
return;
}
contentNode* current = u->files[index]->contentHead;
contentNode* temp;
u->num_files--;
//文件删除磁盘
int i = u->files[index]->contentHead->disk_address, j,k = (u->files[index]->contentHead->disk_address) + (u->files[index]->contentHead->disk_blocknum + 1);
//把此文件删除,磁盘位置整体前移
for (i; i < k; i++)
{
disk_status[i] = 0;
for (j = 1; j < 50; j++)
{
disk[i][j] = '\0';
}
}
null_disk_block += u->files[index]->contentHead->disk_blocknum;
while (current != NULL) {
temp = current;
current = current->next;
free(temp->content);
free(temp);
}
free(u->files[index]->filename);
free(u->files[index]);
for (int i = index; i < u->num_files - 1; i++) {
u->files[i] = u->files[i + 1];
}
printf("文件删除成功\n");
}
// 打开文件
file* open_file(user* u) {
char filename[100];
printf("请输入要打开的文件名:");
fgets(filename, 100, stdin);
filename[strcspn(filename, "\n")] = 0;
for (int i = 0; i < u->num_files; i++) {
if (strcmp(u->files[i]->filename, filename) == 0) {
u->files[i]->flag = true;
printf("文件打开成功\n");
return u->files[i];
}
}
printf("文件不存在\n");
return NULL;
}
// 关闭文件
void close_file(file* f) {
if (f->flag == false) {
printf("文件已经关闭\n");
return;
}
f->flag = false;
printf("文件关闭成功\n");
}
// 读文件
void read_file(file* f) {
if (f->flag == false) {
printf("文件还未打开,请先打开文件\n");
return;
}
if (f->contentHead == NULL) {
printf("文件为空\n");
return;
}
printf("文件内容为:\n");
contentNode* current = f->contentHead;
while (current != NULL) {
printf("%s", current->content);
current = current->next;
}
printf("\n");
}
// 写文件
void write_file(file* f) {
if (f->flag == false) {
printf("文件还未打开,请先打开文件\n");
return;
}
char content[1000];
printf("请输入要写入的内容:");
fgets(content, sizeof(content), stdin);
content[strcspn(content, "\n")] = 0;
contentNode* newNode = (contentNode*)malloc(sizeof(contentNode));
newNode->content = strdup(content);
newNode->disk_address = cur_disk_address;
newNode->next = NULL;
if (f->contentHead == NULL) {
f->contentHead = newNode;
}
else {
contentNode* current = f->contentHead;
while (current->next != NULL) {
current = current->next;
}
current->next = newNode;
}
f->size += strlen(content);
f->length = strlen(content);
//文件写入磁盘
int i = newNode->disk_address, j,k = newNode->disk_address + (f->length/49) + 1;
if (null_disk_block > (((f->length) / 49) + 1))
{
for (i ; i < k; i ++)
{
disk_status[i] = 1;
for (j = 1; j < 50; j++)
{
disk[i][j] = content[j - 1];
}
}
newNode->disk_blocknum = (f->length / 50) + 1;
cur_disk_address = cur_disk_address + newNode->disk_blocknum;
//空磁盘块减
null_disk_block -= newNode->disk_blocknum;
printf("文件写入成功\n");
printf("文件大小为:%d", f->length);
}
else
{
printf("文件写入失败,空间不足\n");
}
}
// 显示用户的文件目录
void display_directory(user* u) {
printf("用户 %s 的文件列表:\n", u->username);
for (int i = 0; i < u->num_files; i++) {
printf("%s\n", u->files[i]->filename);
}
}
// 显示文件
void display_file(user* u) {
file* f = open_file(u);
if (f != NULL) {
read_file(f);
printf("文件大小为:%d\n", f->size);
close_file(f);
}
}
// 转换用户
user* alter_user(user* u) {
printf("请选择用户:\n");
for (int i = 0; i < num_users; i++) {
printf("%d. %s\n", i + 1, users[i].username);
}
scanf("%d", &user_id);
getchar(); // 清除缓冲区
u = &users[user_id - 1];
return u;
}
// 打印菜单
void print_menu() {
printf("*************************************************\n");
printf("请选择操作选项:\n");
printf("1. 创建新用户\t\t");
printf("2. 删除用户\t\t");
printf("3. 展示磁盘存储\n");
printf("4. 登录用户\t\t");
printf("5. 展示用户\t\t");
printf("6. 退出系统\n");
}
void print_menu1() {
printf("*************************************************\n");
printf("请选择操作选项:\n");
printf("1. 创建文件\t\t");
printf("2. 删除文件\t\t");
printf("3. 打开文件\n");
printf("4. 显示目录\t\t");
printf("5. 显示文件\t\t");
printf("6. 切换用户\n");
printf("7. 退出登录\n");
}
void print_menu2() {
printf("*************************************************\n");
printf("请选择操作选项:\n");
printf("1. 关闭文件\t\t");
printf("2. 读文件\t\t");
printf("3. 写文件\n");
}
// 主函数
int main() {
// 文件系统操作选择
int choice;
do {
print_menu();
scanf("%d", &choice);
getchar(); // Clear input buffer
switch (choice) {
case 1:
create_user();
break;
case 2:
delete_user();
break;
case 3:
printf("总文件数量:%d\n", count_files());
break;
case 4:
//login_user();
if (login_user())
{
system("cls");
int choice1;
do {
print_menu1();
scanf("%d", &choice1);
getchar(); // Clear input buffer
switch (choice1) {
case 1:
create_file(u);
break;
case 2:
delete_file(u);
break;
case 3:
curFile = open_file(u);
if (curFile != NULL)
{
system("cls");
int choice2;
do {
print_menu2();
scanf("%d", &choice2);
getchar(); // Clear input buffer
switch (choice2) {
case 1:
close_file(curFile);
system("cls");
//break;
case 2:
read_file(curFile);
break;
case 3:
write_file(curFile);
break;
default:
printf("请输入1-3\n");
break;
}
} while (choice2 != 1);
break;
}
else
{
printf("没有文件\n");
break;
}
case 4:
display_directory(u);
continue;
case 5:
display_file(u);
break;
case 6:
u = alter_user(u);
break;
case 7:
printf("退出文件系统\n");
system("cls");
break;
default:
printf("请输入1-7\n");
break;
}
} while (choice1 != 7);
break;
}
else
{
printf("登录失败\n");
break;
}
case 5:
display_users();
break;
case 6:
printf("退出文件系统\n");
//break;
default:
printf("请输入1-6\n");
break;
}
} while (choice != 6);
return 0;
}
4.实验结果
- 展示磁盘
用二维数组模 disk[50][50] 拟磁盘,上面一排0为当前模拟磁盘状态,0为空1为有数据
- 展示用户
- 登录用户
- 文件操作
- 切换用户
- 再次展示磁盘内容
删除文件
参考资料
https://www.cnblogs.com/peterYong/p/6556614.html
标签:index,操作系统,文件,int,++,PC,实验,cout From: https://www.cnblogs.com/maqun/p/18521679