任务二----------------------------------------------------------------------------------------------------------4
任务三----------------------------------------------------------------------------------------------------------13
任务四----------------------------------------------------------------------------------------------------------19
任务五----------------------------------------------------------------------------------------------------------25
实训总结-------------------------------------------------------------------------------------------------------36
任务一 分析操作系统所面临的操作需求
【实训目的】
让学生可以更好的理解、掌握和应用操作系统中的进程管理、存储管理、设备管理和文件管理等功能。
【实训内容】
1. 熟悉实训环境;
2. 分析操作系统的操作需求;
3. 资料搜集与整理,进行实训的前期准备。
【实训步骤】
1.分析与设计
图1-1 实训总体结构图
【思考题】
1. 操作系统中各模块有怎样的功能?
答:进程管理模块用于分配和控制处理机;设备管理模块主要负责对I/O设备的分配与操纵;文件管理模块主要负责文件的存取、共享和保护;存储管理模块主要负责的分配与回收。
2. 它们之间有怎样的联系?
答:设备管理、文件管理和储存管理都需要进程的管理;文件需要文件管理进行存储,同时也需要储存管理来对文件存储分配空间等等。
3. 针对某一特定的应用环境,如何完善操作系统的功能?
答:要想完善操作系统的功能,必须要合理安排各个功能模块,并利用有效的算法对各个功能进行管理和处理。
任务二 进程管理
【实训目的】
掌握临界区的概念及临界区的设计原则;掌握信号量的概念、PV操作的含义以及应用PV操作实现进程的同步与互斥;分析进程争用资源的现象,学习解决进程互斥的方法;掌握进程的状态及状态转换;掌握常用的进程调度算法。
【实训内容】
1.分析进程的同步与互斥现象,编程实现经典的进程同步问题——生产者消费者问题的模拟;
2.编写允许进程并行执行的进程调度程序,在常用的进程(作业)调度算法:先来先服务算法、短作业优先算法、最高响应比优先算法、高优先权优先算法等调度算法中选择一种调度算法进行简单模拟,并输出平均周转时间和平均带权周转时间。
【实训步骤】
一. 生产者与消费者问题
1.分析与设计
图2-1 生产者与消费者问题分析图
2.程序代码
#include #include const unsigned short BUFFER = 5; //缓冲区长度 unsigned short ProductID = 0; //产品号 unsigned short ConsumeID = 0; //将被消耗的产品号 unsigned short in = 0; //产品进缓冲区时的缓冲区产品个数 unsigned short out = 0; //产品出缓冲区时的缓冲区产品个数 int g_buffer[BUFFER]; //缓冲区为循环队列 bool g_continue = true; //控制程序结束 HANDLE g_hMutex; //线程间互斥对像 HANDLE g_hFullSemaphore; //满则生产者等待 HANDLE g_hEmptySemaphore; //空则消费者等待 DWORD WINAPI Producer(LPVOID); //生产者线程 DWORD WINAPI Consumer(LPVOID); //消费者线程 //主程序 int main() { //创建各个互斥信号 g_hMutex = CreateMutex(NULL,FALSE,NULL); g_hFullSemaphore = CreateSemaphore(NULL,BUFFER-1,BUFFER-1,NULL); g_hEmptySemaphore = CreateSemaphore(NULL,0,BUFFER-1,NULL); const unsigned short PRODUCERS_COUNT = 2; //生产者的个数 const unsigned short CONSUMERS_COUNT = 1; //消费者的个数 //总的线程数 const unsigned short THREADS_COUNT = PRODUCERS_COUNT+CONSUMERS_COUNT; HANDLE hThreads[PRODUCERS_COUNT]; //各线程的handle DWORD producerID[CONSUMERS_COUNT]; //生产者线程的标识符 DWORD consumerID[THREADS_COUNT]; //消费者线程的标识符 //创建生产者线程 for (int i=0;i hThreads[i]=CreateThread(NULL,0,Producer,NULL,0,&producerID[i]); if (hThreads[i]==NULL) return -1; } //创建消费者线程 for (i=0;i hThreads[PRODUCERS_COUNT+i]=CreateThread(NULL,0,Consumer,NULL,0,&consumerID[i]); if (hThreads[i]==NULL) return -1; } while(g_continue) { if(getchar()) { g_continue = false; //按回车后终止程序运行 } } return 0; } //生产一个产品,把新生产的产品放入缓冲区 void Produce() { std::cerr << "生产产品 " << ++ProductID << std::endl; std::cerr << "将新的产品"; g_buffer[in] = ProductID; in = (in+1)%BUFFER; std::cerr << "放入缓冲区" << std::endl; //输出缓冲区当前的状态 for (int i=0;i std::cout << i <<": " << g_buffer[i]; if (i==in) std::cout << " ← 生产"; if (i==out) std::cout << " ← 消费"; std::cout << std::endl; } } //从缓冲区中取出一个产品,并将其消耗 void Consume() { std::cerr << "从缓冲区中取出产品"; ConsumeID = g_buffer[out]; out = (out+1)%BUFFER; std::cout << std::endl; //输出缓冲区当前的状态 for (int i=0;i std::cout << i <<": " << g_buffer[i]; if (i==in) std::cout << " ← 生产"; if (i==out) std::cout << " ← 消费"; std::cout << std::endl; } std::cerr << "消耗一个产品 " << ConsumeID << std::endl; } //生产者 DWORD WINAPI Producer(LPVOID lpPara) { while(g_continue) { WaitForSingleObject(g_hFullSemaphore,INFINITE); WaitForSingleObject(g_hMutex,INFINITE); Produce(); Sleep(1500); //此处以毫秒为单位 ReleaseMutex(g_hMutex); ReleaseSemaphore(g_hEmptySemaphore,1,NULL); } return 0; } //消费者 DWORD WINAPI Consumer(LPVOID lpPara) { while(g_continue) { WaitForSingleObject(g_hEmptySemaphore,INFINITE); WaitForSingleObject(g_hMutex,INFINITE); Consume(); Sleep(1500); //此处以毫秒为单位 ReleaseMutex(g_hMutex); ReleaseSemaphore(g_hFullSemaphore,1,NULL); } return 0; } 3.程序运行截图 图2-2 生产者与消费者问题运行截图 二.先来先服务算法 1.分析与设计 图2-3 先来先服务算法设计图 2.程序代码 #include //定义进程的结构体 struct fcfs { char name[10]; //进程名称 int priority; //进程优先数 float arrivetime; //到达时间 float servicetime; //运行时间 float starttime; //开始时间 float finishtime; //完成时间 float returntime; //周转时间 float wreturntime; //带权周转时间 }; fcfs a[50]; //程序的输入显示及提示 void input(fcfs *p,int N) { int i; printf("请依次输入 进程名称→到达时间→运行时间→进程优先数:\\n"); for(i=0;i<=N-1;i++) { printf("\\n输入%d号进程信息:\\n",i+1); scanf("%s%f%f%d",&p[i].name,&p[i].arrivetime,&p[i].servicetime,&p[i].priority); } } //程序的输出显示 void Print(fcfs *p,float arrivetime,float servicetime,float starttime,float finishtime,float returntime,float wreturntime,int priority,int N) { int k; printf("运行指令:"); printf("%s",p[0].name); for(k=1;k printf("→%s",p[k].name); } printf("\\n进程信息:\\n"); printf("\\n进程\到达\运行\开始\结束\周转\带权周转 进程优先数\\n"); for(k=0;k<=N-1;k++) { printf("%s\%-.2f\%-.2f\%-.2f\%-.2f\%-.2f\ %-.2f\\%d\\n",p[k].name ,p[k].arrivetime,p[k].servicetime,p[k].starttime,p[k].finishtime,p[k] .returntime,p[k].wreturntime,p[k].priority); } } //排序采用冒泡排序法进行排序,排序顺序从小到大 void sort(fcfs *p,int N) { for(int i=0;i<=N-1;i++) for(int j=0;j<=i;j++) if(p[i].arrivetime { fcfs temp; temp=p[i]; p[i]=p[j]; p[j]=temp; } } //运行阶段 void funciton(fcfs *p, float arrivetime,float servicetime,float starttime,float finishtime,float &returntime,float &wreturntime,int priority,int N) { int k; for(k=0;k<=N-1;k++) { if(p[k].arrivetime>p[k-1].finishtime) { p[k].starttime=p[k].arrivetime; p[k].finishtime=p[k].arrivetime+p[k].servicetime; } else { p[k].starttime=p[k-1].finishtime; p[k].finishtime=p[k-1].finishtime+p[k].servicetime; } } for(k=0;k<=N-1;k++) { p[k].returntime=p[k].finishtime-p[k].arrivetime; p[k].wreturntime=p[k].returntime/p[k].servicetime; } } //模拟操作系统的先来先服务算法 void FCFS(fcfs *p,int N) { float arrivetime=0,servicetime=0,starttime=0,finishtime=0,returntime=0,wreturntime=0,priority=0; sort(p,N); funciton(p,arrivetime,servicetime,starttime,finishtime,returntime,wreturntime,priority,N); Print(p,arrivetime,servicetime,starttime,finishtime,returntime,wreturntime,priority,N ); } //主函数 void main() { int N; printf("\\\进程调度 之 先来先服务调度算法\\n"); printf("请输入进程数:\\n"); scanf("%d",&N); input(a,N); FCFS(a,N); } 3.程序运行截图 图2-4 先来先服务调度算法运行截图 【思考题】 1. 针对某一具体应用环境,如何选择合适的调度算法? 答:应根据具体情况来选用合适的调度算法。比如,在批处理系统中,为了照顾为数众多的短作业,应采用短作业优先调度的调度算法;在分时系统中,为了保证系统具有合理的响应时间,应采用轮转法进行调度。非抢占式调度算法,有利于长作业,不利于短作业。 任务三 存储管理 【实训目的】 掌握物理内存和虚拟内存的基本概念;掌握重定位的基本概念及其要点,理解逻辑地址与绝对地址;掌握各种存储管理的实现方法,包括基本原理、地址变换和缺页中断、主存空间的分配及分配算法;掌握常用淘汰算法。 【实训内容】 编写一个模拟的动态页式存储管理程序,实现对动态页式存储的淘汰算法的模拟(在先进先出淘汰算法、最近最少使用淘汰算法、最不经常使用淘汰算法三种算法中选择一种进行模拟)并计算各个算法的缺页率;并且页面淘汰算法在淘汰一页时,只将该页在页表中抹去,而不再判断它是否被改写过,也不将它写回到辅存 【实训步骤】 1.分析与设计 设置一个后进先出栈,栈大小为分配给这个进程的页面数。在在系统中设定一个计数器,进程进程进行访问内页面操作都把计数器的值加1,把结果值赋值给访问的页面,在淘汰页面时选择计数器值最小的页面淘汰。这样的栈顶上总是保存着最近被访问的页面号,而栈底保存的就是最近最久没有被访问的页面号。如图3-1所示 图3-1最近最久未使用置换算法分析图 2.程序代码 #include #include //最近最久未使用置换算法 //参数说明:p地址流,n地址流的个数,pageSize页面大小,pageTable页表,count页表大小 void LRU (int p[], int n, int pageSize, int pageTable[], int count) { int i, pageNo, j, pagecount, k; float sum; int *stack = (int *)malloc (sizeof (int) * count); pagecount = 0; k = 0; sum = 0; system ("cls"); printf ("\\n\\n\\\存储管理---最近最少使用淘汰算法\\n\\n"); for (i = 0; i < n; i++) { pageNo = p[i] / pageSize; printf ("\\调入页号%d后\ ", pageNo); printf ("\\页表:"); for (j = 0; j < pagecount; j++)//判断页号是否在页表中 { if (pageNo == pageTable[j])//如果页号在页表中 { for (k = 0; k < count; k++)//设置栈中各页面使用情况 { if (stack[k] == pageNo) { if (k != count-1) { for (; k < count-1; k++) { stack[k] = stack[k+1]; } stack[k] = pageNo; } } } break; } } //页号不在页表中,插入页表 if (j == pagecount) { //如果页表不满 if (pagecount < count) { pageTable[pagecount++] = pageNo; stack[pagecount-1] = pageNo; } //如果页表已满 else { for (j = 0; j < count; j++) { if (pageTable[j] == stack[0]) { pageTable[j] = pageNo; for (k = 0; k < count-1; k++)//设置栈中各页面使用情况 { stack[k] = stack[k+1]; } stack[k] = pageNo; break; } } } sum++; //缺页数 } //打印页表 for (j = 0; j < count; j++) { if (pageTable[j] >= 0) { printf ("%d ", pageTable[j]); } } printf ("\\n"); } sum /= n; sum *= 100; printf ("\\\缺页率:%.1f%%\\n\\n", sum); free (stack); } void main () { int n, pageSize = 1024, pageTable[3]; int *p, i; FILE *fp; system ("cls"); printf ("\\n\\n\\\存储管理---最近最少使用淘汰算法\\n\\n\\n"); printf ("请确认在\\"Address.txt\\"文件中已输入地址流.\\n"); printf ("如果没有,请自行新建后再运行.\\n\\n\\n\\n"); system ("pause"); if ((fp = fopen ("Address.txt", "r+")) == NULL) { printf ("打开文件出错!\\n"); system ("pause"); return ; } fscanf (fp, "%d", &n); p = (int *)malloc (sizeof (int) * n); printf ("\\n\\n\\n读取到以下的地址流:\\n"); for (i = 0; i < n; i++) { fscanf (fp, "%d", &p[i]); printf ("%d ", p[i]); } printf ("\\n\\n"); fclose (fp); system ("pause"); system ("cls"); LRU (p, n, pageSize, pageTable, 3); } 3.程序运行截图 图3-2最近最久未使用置换算法程序截图 图3-3最近最久未使用置换算法程序截图 【思考题】 1. 各种不同的页面淘汰算法有哪些优缺点?为什么会产生页面抖动?什么是belady 现象?这种现象该如何避免? 答:最佳值换算法其所选择的被淘汰页将是以后用不适用的,或许是在最长(未来)时间内不再被访问的页面。采用最佳值换算法通常可保证获得最低的缺页率。但是由于人们目前还无法预知一个进程在内存的若干个页面中,哪一个页面是未来最长时间内不再被访问的,因此该算法是无法实现的,但可以利用该算法去评价其它算法。先进先出置换算法(FIFO)是最直观的算法,由于它可能是性能最差的算法,故实际应用极少。最近最久未使用置换算法(LRU)虽然是一种比较好的算法,但要求系统有较多的支持硬件。 因为刚被淘汰出去的页,过后不久又要访问它,需要再次调入,而调入后不久又再次被淘汰,然后又要访问,如此反复,使得系统的把大部分时间用在页面的调进调出上,这种形象称为“抖动”。 随着物理块数的增多缺页率增大,从而造成Belady异常现象。尽量避免物理块数不断增多缺页率最大。 任务四 设备管理 【实训目的】 掌握独占设备的使用方式,以及设备的分配和回收;掌握用死锁避免方法来处理申请独占设备可能带来死锁。 【实训内容】 用死锁避免方法来处理申请独占设备可能造成的死锁,程序实现对银行家算发的模拟。 【实训步骤】 1、分析与设计 1.1、银行家算法: 设进程x提出请求Request[y],则银行家算法按如下规则进行判断。 (1)如果Request[y]>Need[x,y],则报错返回。 (2)如果Request[y]>Available,则进程i进入等待资源状态,返回。 (3)假设进程n的申请已获批准,于是修改系统状态: Available=Available-Request Allocation=Allocation+Request Need=Need-Request (4)系统执行安全性检查,如安全,则分配成立;否则试探险性分配作废,系统恢复原状,进程等待。 1.2、安全性检查: (1)设置两个工作向量Work=Available;Finish[z]=False (2)从进程集合中找到一个满足下述条件的进程, Finish [x]=False Need<=Work 如找到,执行(3);否则,执行(4) (3)设进程获得资源,可顺利执行,直至完成,从而释放资源。 Work=Work+Allocation Finish=True GO TO 2 (4)如所有的进程Finish[z]=true,则表示安全;否则系统不安全 1.3银行家算法实现流程图,如图4-1所示。 图4-1银行家算法实现流程图 2、程序代码 #include #include #include #define X 5 //总进程数 #define Y 3 //总资源数 //银行家算法定义结构体 struct banker { int max[X][Y]; //最大需求矩阵 int allocation[X][Y]; //分配矩阵 int need[X][Y]; //需求矩阵 int available[Y]; //可利用资源向量 }; //初始化 void initilize (banker &x) { int i,j; printf("请输入数据(系统设定总进程数为5,总资源数为3):\\n"); printf("最大需求矩阵:\\n"); for(i=0;i for(j=0;j scanf("%d",&x.max[i][j]); } } printf("分配矩阵:\\n"); for(i=0;i for(j=0;j scanf("%d",&x.allocation[i][j]); } } for(i=0;i for(j=0;j x.need[i][j]=x.max[i][j]-x.allocation[i][j]; } } printf("可利用资源向量:\\n"); for(i=0;i scanf("%d",&x.available[i]); } } //检查安全性 int safe (banker x) { int i,j; int safeprocess[X]; //安全序列向量 int work[Y]; //空闲资源矩阵 int Finish[X]; //进程完成标志矩阵 for(i=0;i for(i=0;i int k= 0; //安全序列排列号 for(i=0;i if(Finish[i]==false) { for(j=0;j if(x.need[i][j]>work[j]) //判断当前进程需求矩阵能否得到满足 break; //不满足则跳出 } if(j==Y) //第i个进程满足执行条件 { safeprocess[k++]=i; //将进程号存入安全序列向量 for(int q=0;q Finish[i]=true; //标志该进程可完成 i=-1; //下次检查从第一个进程重新查起 } } } for(i=0;i { printf("无法找到安全序列,系统处于不安全状态!\\n"); return 0; } printf("安全序列为:"); //找到安全序列并显示该序列 for(i=0;i printf("\\n"); return 1; } //系统对进程资源申请的处理 void allocate(banker &x) { banker temp= x; //临时变量存储x的初值 int Request[Y]; //请求向量 int number; //进程号 int i; printf("请输入要申请资源的进程序号:\\n"); scanf("%d",&number); printf("请输入请求向量:\\n"); for(i=0;i for(i=0;i if(Request[i]>x.need[number-1][i])//所需资源数大于需求量 { printf("错误! 进程所需要的资源数已超过最大值。\\n"); return ; } if(Request[i]>x.available[i]) //所需资源数大于可利用资源 { printf("错误! 系统中没有足够的资源满足进程的申请。\\n"); return ; } } for(i=0;i x.available[i] -= Request[i]; x.need[number-1][i] -= Request[i]; x.allocation[number-1][i] += Request[i]; } if(safe(x)) //安全性检查结果为安全 { printf("系统可以为该进程分配资源.\\n"); return ; } else //安全性检查结果为不安全 { printf("系统不为该进程分配资源\\n"); x=temp; //将相关矩阵修改过来,表示资源不分配资源 return ; } } //主程序 void main(void) { banker current; //定义变量 initilize(current); //初始化 safe(current); //检查安全性 while(1) //循环执行进程申请资源和系统对申请的处理 { allocate(current); } } 3、运行并测试程序,并给出程序运行界面。 图4-2 银行家算法运行截图 【思考题】 1. 如果产生了死锁,应如何解除? 答:当发现有死锁时,便应立即把它们从死锁状态中解脱出来。常采用解除死锁的两种方法是: (1)剥夺资源。从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态。 (2)撤销进程。最简单的撤销进程的方法是使全部死锁状态进程都夭折掉;稍微温和一点的方法是按照某种顺序逐个的撤销进程,直至有足够的资源可用,使死锁状态消除为止。 任务五 文件管理 【实训目的】 掌握文件的存取方法;掌握文件的逻辑结构和物理结构;掌握存储空间的分配和回收;掌握磁盘管理与调度。 【实训内容】 用程序模拟磁盘的调度过程,并计算各磁盘调度算法包括先来先服务算法、最短寻道时间优先算法、扫描算法和循环扫描算法的平均寻道长度。 【实训步骤】 1、分析与设计 图5-1 先来先服务算法流程图 图5-2 最短寻道时间算法流程图 图5-3 扫描算法流程图 2、程序代码 #include "stdio.h" #include "stdlib.h" int NAll=0; int Best[5][2]; //用作寻道长度由低到高排序时存放的数组 int Limit=0; //输入寻找的范围磁道数i int Jage; float Aver=0; //数组Sour复制到数组Dist,复制到x个数 void CopyL(int Sour[],int Dist[] ,int x) { int i; for(i=0;i<=x;i++) { Dist[i]=Sour[i]; } } //打印输出数组 void Print(int Pri[],int x) { int i; for(i=0;i<=x;i++) { printf("%5d",Pri[i]); } } //随机生成磁道数 void SetDI(int DiscL[]) { int i; for(i=0;i<=9;i++) { DiscL[i]=rand()%Limit;//随机生成10个磁道号 } system("cls"); printf("\\n\\n\\n需要寻找的磁道号:"); Print(DiscL,9); //输出随机生成的磁道号 printf("\\n"); } //数组Sour把x位置的数删除,并把y前面的数向前移动 void DelInq(int Sour[],int x,int y) { int i; for(i=x;i Sour[i]=Sour[i+1]; x++; } } //先来先服务算法(FCFS) void FCFS(int Han,int DiscL[]) { int RLine[10]; //将随机生成的磁道数数组Discl[]复制给数组RLine[] int i,k,All,Temp; //Temp是计算移动的磁道距离的临时变量 All=0; //统计全部的磁道数变量 k=9; //限定10个的磁道数 CopyL(DiscL,RLine,9); //复制磁道号到临时数组RLine printf("\\n按照FCFS算法磁道的访问顺序为:"); All=Han-RLine[0]; for(i=0;i<=9;i++) { Temp=RLine[0]-RLine[1];//求出移动磁道数,前一个磁道数减去后一个磁道数得出临时的移动距离 if(Temp<0) Temp=(-Temp);//移动磁道数为负数时,算出相反数作为移动磁道数 printf("%5d",RLine[0]); All=Temp+All;//求全部磁道数的总和 DelInq(RLine,0,k);//每个磁道数向前移动一位 k--; } Best[Jage][1]=All;//Best[][1]存放移动磁道数 Best[Jage][0]=1; //Best[][0]存放算法的序号为:1 Jage++;//排序的序号加1 Aver=((float) All)/10;//求平均寻道次数 printf("\\n移动磁道数:<%5d> ",All); printf("\\n平均寻道长度:*%0.2f* ",Aver); } //最短寻道时间优先算法(SSTF) void SSTF(int Han,int DiscL[]) { int i,j,k,h,All; int Temp; //Temp是计算移动的磁道距离的临时变量 int RLine[10]; //将随机生成的磁道数数组Discl[]复制给数组RLine[] int Min; All=0; //统计全部的磁道数变量 k=9; //限定10个的磁道数 CopyL(DiscL,RLine,9); //复制磁道号到临时数组RLine printf("\\n按照SSTF算法磁道的访问顺序为:"); for(i=0;i<=9;i++) { Min=000; for(j=0;j<=k;j++) //内循环寻找与当前磁道号最短寻道的时间的磁道号 { if(RLine[j]>Han) //如果第一个随机生成的磁道号大于当前的磁道号,执行下一句 Temp=RLine[j]-Han; //求出临时的移动距离 else Temp=Han-RLine[j]; //求出临时的移动距离 if(Temp Min=Temp; //Temp临时值赋予Min h=j; //把最近当前磁道号的数组下标赋予h } } All=All+Min; //统计一共移动的距离 printf("%5d",RLine[h]); Han=RLine[h]; DelInq(RLine,h,k); //每个磁道数向前移动一位 k--; } Best[Jage][1]=All;//Best[][1]存放移动磁道数 Best[Jage][0]=2;//Best[][0]存放算法的序号为:2 Jage++;//排序序号加1 Aver=((float)All)/10;//求平均寻道次数 printf("\\n移动磁道数:<%5d> ",All); printf("\\n平均寻道长度:*%0.2f* ",Aver); } //扫描算法(SCAN) int SCAN(int Han,int DiscL[],int x,int y) { int j,n,k,h,m,All; int t=0; int Temp; int Min; int RLine[10]; //将随机生成的磁道数数组Discl[]复制给数组RLine[] int Order; Order=1; k=y; m=2; //控制while语句的执行,即是一定要使当前磁道向内向外都要扫描到 All=0; //统计全部的磁道数变量 CopyL(DiscL,RLine,9); //复制磁道号到临时数组RLine printf("\\n按照SCAN算法磁道的访问顺序为:"); Min=000; for(j=x;j<=y;j++) //寻找与当前磁道号最短寻道的时间的磁道号 { if(RLine[j]>Han) //如果第一个随机生成的磁道号大于当前的磁道号,执行下一句 Temp=RLine[j]-Han; //求出临时的移动距离 else Temp=Han-RLine[j]; //求出临时的移动距离 if(Temp Min=Temp; //Temp临时值赋予Min h=j; //把最近当前磁道号的数组下标赋予h } } All=All+Min; printf("%5d",RLine[h]); if(RLine[h]>=Han){ //判断磁道的移动方向,即是由里向外还是由外向里 Order=0; t=1; } Han=RLine[h]; DelInq(RLine,h,k); //每个磁道数向前移动一位 k--; while(m>0) { if(Order==1) //order是判断磁盘扫描的方向标签,order是1的话,磁道向内移动 { for(j=x;j<=y;j++) { h=-1; Min=000; for(n=x;n<=k;n++) //判断离当前磁道最近的磁道号 { if(RLine[n]<=Han) { Temp=Han-RLine[n]; if(Temp Min=Temp; //Temp临时值赋予Min h=n; //把最近当前磁道号的数组下标赋予h } } } if(h!=-1) { All=All+Min; //叠加移动距离 printf("%5d",RLine[h]); Han=RLine[h]; //最近的磁道号作为当前磁道 DelInq(RLine,h,k); k--; } } Order=0; //当完成向内的移动,order赋予0,执行else语句,使磁道向外移动 m--; //向内完成一次,m减一次,保证while循环执行两次 } else //order是0的话,磁道向外移动 { for(j=x;j<=y;j++) { h=-1; Min=000; for(n=x;n<=k;n++) //判断离当前磁道最近的磁道号 { if(RLine[n]>=Han) { Temp=RLine[n]-Han; if(Temp Min=Temp; //Temp临时值赋予Min h=n; //把最近当前磁道号的数组下标赋予h } } } if(h!=-1) { All=All+Min; //叠加移动距离 printf("%5d",RLine[h]); Han=RLine[h]; //最近的磁道号作为当前磁道 DelInq(RLine,h,k); k--; } } Order=1; //当完成向内的移动,order赋予0,执行else语句,使磁道向外移动 m--; //向内完成一次,m减一次,保证while循环执行两次 } } NAll=NAll+All; if((y-x)>5) { Best[Jage][1]=All;//Best[][1]存放移动磁道数 Best[Jage][0]=3;//Best[][0]存放算法的序号为:3 Jage++;//排序序号加1 Aver=((float)All)/10;//求平均寻道次数 printf("\\n移动磁道数:<%5d> ",All); printf("\\n平均寻道长度:*%0.2f* ",Aver); } if(t==1) printf("\\n磁道由内向外移动"); else printf("\\n磁道由外向内移动"); return(Han); } //寻道长度由低到高排序 void Sort() { int i,j,Temp; for(i=0;i<5;i++) { for(j=0;j<4;j++) { if(Best[j][1]>Best[j+1][1]) //如果前一个算法的移动磁道距离大于后一个移动磁道数,执行下面语句 { Temp=Best[j+1][1]; //从这起下三行执行冒泡法将移动距离大小排序,排完后则执行每个算法的排序 Best[j+1][1]=Best[j][1]; Best[j][1]=Temp; Temp=Best[j+1][0]; //将每个算法的序号用冒泡法排序 Best[j+1][0]=Best[j][0]; Best[j][0]=Temp; } } } } void main() { int i; int DiscLine[10]; //声明准备要生成的随机磁道号的数组 int Hand; //磁道数 int Con=1; int n; while(Con==1) { Jage=0; system("cls"); printf("\\n\\n\取值范围是0-65536:"); printf("\\n\\n\请输入初始的磁道数:"); scanf("%d",&Hand); printf("\\n\请输入寻找的范围:"); scanf("%d",&Limit); if(Limit>65536 || Hand>65536) { printf("超出范围!"); } else{ system("cls"); printf("\\n\\n\\n\\n\\n"); printf("\ ************************主菜单***************************\\n"); printf("\ ** **\\n"); printf("\ ** 1.先来先服务算法(FCFS) **\\n"); printf("\ ** **\\n"); printf("\ ** 2.最短寻道时间优先算法(SSTF) **\\n"); printf("\ ** **\\n"); printf("\ ** 3.扫描算法(SCAN) **\\n"); printf("\ ** **\\n"); printf("\ ** 4.各类算法的比较 **\\n"); printf("\ ** **\\n"); printf("\ ** 0.退出程序 **\\n"); printf("\ ** **\\n"); printf("\ *********************************************************\\n"); scanf("%d",&n); if(n==0) exit(0); printf("\\n"); switch(n) { case 1: SetDI(DiscLine); //随机生成磁道数 FCFS(Hand,DiscLine); //先来先服务算法(FCFS) break; case 2: SetDI(DiscLine); //随机生成磁道数 SSTF(Hand,DiscLine); //最短寻道时间优先算法(SSTF) break; case 3: SetDI(DiscLine); //随机生成磁道数 SCAN(Hand,DiscLine,0,9); //扫描算法(SCAN) break; case 4: SetDI(DiscLine); //随机生成磁道数 FCFS(Hand,DiscLine); //先来先服务算法(FCFS) SSTF(Hand,DiscLine); //最短寻道时间优先算法(SSTF) SCAN(Hand,DiscLine,0,9); //扫描算法(SCAN) Sort(); //寻道长度由低到高排序 printf("\\n\\n寻道长度由低到高排序:"); for(i=0;i<5;i++) { printf("%4d ",Best[i][0]); } break; } printf("\\n\\n是否继续(按0结束,按1继续)?"); scanf("%5d",&Con); } } return ; } 3、运行并测试程序,并给出程序运行界面。 图5-4 三种算法对比运行截图 图5-5 三种算法对比运行截图 图5-6 三种算法对比运行截图 【思考题】 如何在文件管理模块增加如下功能: 1. 改变目录:格式:cd <目录名> 2. 显示目录:格式:dir [<目录名>] 3. 创建目录:格式:md <目录名> 4. 除目录:格式:rd <目录名> 5. 新建文件:格式:edit <文件名> 6. 删除文件:格式:del <文件名> 7. 退出文件系统:exit 答,通过输入函数进行命令读取,在程序内部进行命令的执行,以实现到各命令功能。 实训总结 这一次的实训内容牵涉到了进程管理、文件管理、设备管理和存储管理四个大的功能模块,每当我要完成一个功能模块,都要相应的熟悉了解知识点,让我无论是在操作系统方面,还是编程语言方面都有所进步。 通过本次操作系统的程序设计使我认识到要将操作系统这门计算机专业的课学好不仅仅是要把书上的基本知识学好而且还要不断进行实践,将所学的跟实践操作结合起来才能更好地巩固所学,才能提高自己实践能力.通过这次的设计使我认识到只停留在表面理解问题是很难使问题得到很好的解决的,实践能力与理论知识同样重要。可以说此程序设计的理论难度并不大,但是若要深入发掘其中的东西,并且实际去编程实现,就遇到了相当大的难度。因为与之涉及的很多方面并没有学过,需要自己去自学和实践检验。 最后,我非常高兴有这样的一次实训机会,同时也感谢老师为我们准备的实训题。这次实训让我学到了很多在书本上学不到的东西,同时也让我了解到了自己还有很多的缺点和不足。