第一篇:操作系统存储管理实验介绍
河南师范大学计算机与信息技术学院实验报告
实验五 存储管理
一、实验目的、加深对操作系统存储管理的理解、能过模似页面调试算法,加深理解操作系统对内存的高度管理
二、总的设计思想、环境语言、工具等总的设计思想:
1、编写函数计算并输出下述各种算法的命中率
① OPT页面置换算法
OPT所选择被淘汰的页面是已调入内存,且在以后永不使用的,或是在最长时间内不再被访问的页面。因此如何找出这样的页面是该算法的关键。可为每个页面设置一个步长变量,其初值为一足够大的数,对于不在内存的页面,将其值重置为零,对于位于内存的页面,其值重置为当前访问页面与之后首次出现该页面时两者之间的距离,因此该值越大表示该页是在最长时间内不再被访问的页面,可以选择其作为换出页面。② FIFO页面置换算法
FIFO总是选择最先进入内存的页面予以淘汰,因此可设置一个先进先出的忙页帧队列,新调入内存的页面挂在该队列的尾部,而当无空闲页帧时,可从该队列首部取下一个页帧作为空闲页帧,进而调入所需页面。③ LRU页面置换算法
LRU是根据页面调入内存后的使用情况进行决策的,它利用“最近的过去”作为“最近的将来”的近似,选择最近最久未使用的页面予以淘汰。该算法主要借助于页面结构中的访问时间time来实现,time记录了一个页面上次的访问时间,因此,当须淘汰一个页面时,选择处于内存的页面中其time值最小的页面,即最近最久未使用的页面予以淘汰。
④ LFU页面置换算法
LFU要求为每个页面配置一个计数器(即页面结构中的counter),一旦某页被访问,则将其计数器的值加1,在需要选择一页置换时,则将选择其计数器值最小的页面,即内存中访问次数最少的页面进行淘汰。⑤ NUR页面置换算法
NUR要求为每个页面设置一位访问位(该访问位仍可使用页面结构中的counter表示),当某页被访问时,其访问位counter置为1。需要进行页面置换时,置换算法从替换指针开始(初始时指向第一个页面)顺序检查处于内存中的各个页面,如果其访问位为0,就选择该页换出,否则替换指针下移继续向下查找。如果内存中的所有页面扫描完毕未找到访问位为0的页面,则将替换指针重新指向第一个页面,同时将内
河南师范大学计算机与信息技术学院实验报告
存中所有页面的访问位置0,当开始下一轮扫描时,便一定能找到counter为0的页面。
2、在主函数中生成要求的指令序列,并将其转换成页地址流;在不同的内存容量下调用上述函数使其计算并输出相应的命中率。
环境语言:Linux下的GNU 编译环境
三、数据结构与模块说明
程序中用到的数据结构、类型定义及主要的函数原型如下:
1、数据结构
(1)页面结构 typedef struct{ int pn, pfn, counter, time;} pl_type;pl_type pl[total_vp];其中pn为页面号(页号),pfn为页帧号(物理块号),counter为一个周期内访问该页面的次数,time为访问时间;pl[total_vp]为页面结构数组,由于共有320条指令,每页可装入10条指令,因此虚页长total_vp的值为32。
(2)页帧控制结构 struct pfc_struct{ int pn, pfn;struct pfc_struct *next;};typedef struct pfc_struct pfc_type;pfc_type pfc[total_vp], *freepf_head, *busypf_head, *busypf_tail;其中pfc[total_vp]定义用户进程的页帧控制结构数组,在该实验中,用户内存工作区是动态变化的,最多可达到用户进程的虚页数目,即32个物理块。
*freepf_head为空闲页帧头的指针 *busypf_head为忙页帧头的指针 *busypf_tail忙页帧尾的指针
2、变量定义
(1)int a[total_instruction]: 指令流数组(2)int diseffect: 页面失效次数
(3)int page[total_instruction]: 每条指令所属页面号
(4)int offset[total_instruction]: 每页装入10条指令后取模运算得出的页内偏移地址(5)int total_pf: 用户进程的内存页帧数
河南师范大学计算机与信息技术学院实验报告
3、主要函数
(1)void initialize(int): 初始化函数
该函数主要对页面结构数组pl和页帧结构数组pfc进行初始化,如置页面结构中的页面号pn,初始化页帧号pfn为空,访问次数counter为0,访问时间time为-1;同样对页帧数组进行初始化,形成一个空闲页帧队列。
(2)void OPT(int): 计算使用最佳页面算法时的命中率
(3)void FIFO(int): 计算使用先进先出页面置换算法时的命中率(4)void LRU(int): 计算使用最近最久未使用页面置换算法时的命中率(5)void LFU(int): 计算使用最少使用置换算法时的命中率(6)void NUR(int): 计算使用最近未使用置换算法时的命中率
四、主要算法的设计与实现
void FIFO(int total_pf)/*先进先出页面置换算法*/ { int i,j;pfc_type *p;initialize(total_pf);busypf_head=busypf_tail=NULL;for(i=0;i if(pl[page[i]].pfn==INVALID)/*页面失效*/ { diseffect=diseffect+1; if(freepf_head==NULL)/*无空闲页帧*/ { } p=freepf_head->next;//有空闲页帧 freepf_head->next=NULL;freepf_head->pn=page[i];/* 将所需页面调入空闲页帧 */ pl[page[i]].pfn=freepf_head->pfn;if(busypf_tail==NULL)/* 若忙页帧队列为空,则将其头尾指针都指向刚调入页p=busypf_head->next;pl[busypf_head->pn].pfn=INVALID;//将忙页帧队首页面作为换出页面 freepf_head=busypf_head;freepf_head->next=NULL;busypf_head=p;//忙页帧头指针后移 面所在的页帧 */ 河南师范大学计算机与信息技术学院实验报告 busypf_head=busypf_tail=freepf_head;else{ //否则,将刚调入页面所在的页帧挂在忙页帧队列尾部 } freepf_head=p;//空闲页帧头指针后移 busypf_tail->next=freepf_head;busypf_tail=freepf_head;} } printf(“FIFO:%6.4f ”,1-(float)diseffect/320);} void LRU(int total_pf)/*最近最久未使用页面置换算法*/ { int i,j;int min,minj,present_time;initialize(total_pf);present_time=0;for(i=0;i if(pl[page[i]].pfn==INVALID)/*页面失效*/ { diseffect++;if(freepf_head==NULL)/*无空闲页帧*/ { min=32767;for(j=0;j } freepf_head=&pfc[pl[minj].pfn];//腾出一个单元 pl[minj].pfn=INVALID;pl[minj].time=-1;freepf_head->next=NULL;if(min>pl[j].time && pl[j].pfn!=INVALID){ } min=pl[j].time;minj=j;面*/ } pl[page[i]].pfn=freepf_head->pfn;//有空闲页面,改为有效 pl[page[i]].time=present_time;//修改页面的访问时间 河南师范大学计算机与信息技术学院实验报告 } freepf_head=freepf_head->next;//减少一个free 页面 else pl[page[i]].time=present_time;//命中则修改该单元的访问时间 present_time++;} printf(“LRU:%6.4f ”,1-(float)diseffect/320);} void NUR(int total_pf)/* 最近未使用页面置换算法 */ { int i,j,dp,cont_flag,old_dp;initialize(total_pf);dp=0;for(i=0;i if(pl[page[i]].pfn==INVALID)/*页面失效*/ { diseffect++;if(freepf_head==NULL)/*无空闲页帧*/ { cont_flag=TRUE;old_dp=dp;while(cont_flag){ if(pl[dp].counter==0&&pl[dp].pfn!=INVALID) cont_flag=FALSE;//找到位于内存且未被访问的页面 else { dp++; if(dp==total_vp)dp=0;//将替换指针重新指向第一个页面 if(dp==old_dp) {/* 若内存中所有页面扫描完毕未找到访问位为0的页面,将内存中所有页面的访问位置0 */ } freepf_head=&pfc[pl[dp].pfn];//腾出一个单元 } } for(j=0;j pl[j].counter=0; 河南师范大学计算机与信息技术学院实验报告 } pl[dp].pfn=INVALID;freepf_head->next=NULL; } pl[page[i]].pfn=freepf_head->pfn;//有空闲页面,改为有效 freepf_head=freepf_head->next;//减少一个free 页面 else pl[page[i]].counter=1;//命中则将访问位置1 if(i%clear_period==0)//清零周期到,将所有访问位清零 { for(j=0;j } } } void OPT(int total_pf)/* 最佳页面置换算法 */ { int i,j,max,maxpage,d,dist[total_vp];initialize(total_pf);for(i=0;i for(j=0;j } d=1;/* 对于位于内存且在当前访问页面之后将再次被访问的页面,dist重置为当前页 面与之后首次出现该页面时两者之间的距离 */ for(j=i+1;j dist[j]=32767;printf(“NUR:%6.4f ”,1-(float)diseffect/320); else //不在内存的页面该变量则置为0 dist[j]=0; 河南师范大学计算机与信息技术学院实验报告 } } if(pl[page[j]].pfn!=INVALID && dist[page[j]]==32767) dist[page[j]]=d; d++;max=-1;//查找dist变量值最大的页面作为换出页面 for(j=0;j } freepf_head=&pfc[pl[maxpage].pfn];//腾出一个单元 freepf_head->next=NULL;pl[maxpage].pfn=INVALID;if(max } max=dist[j];maxpage=j; } } pl[page[i]].pfn=freepf_head->pfn;//有空闲页面,改为有效 freepf_head=freepf_head->next;//减少一个free 页面 printf(“OPT:%6.4f ”,1-(float)diseffect/320);} void LFU(int total_pf)/* 最少使用页面置换算法 */ { int i,j,min,minpage;initialize(total_pf);for(i=0;i min=32767;for(j=0;j if(min>pl[j].counter&&pl[j].pfn!=INVALID){ 河南师范大学计算机与信息技术学院实验报告 } } } min=pl[j].counter;minpage=j;pl[j].counter=0; freepf_head=&pfc[pl[minpage].pfn];//腾出一个单元 pl[minpage].pfn=INVALID;freepf_head->next=NULL; pl[page[i]].pfn=freepf_head->pfn;//有空闲页面,改为有效 pl[page[i]].counter++;//增加页面访问次数 freepf_head=freepf_head->next;//减少一个free 页面 } else pl[page[i]].counter++;//命中增加页面访问次数 } printf(“LFU:%6.4f ”,1-(float)diseffect/320);} 五、运行结果 本实验的运行结果如下图所示(以OPT、FIFO、LRU为例): 从上述结果可知,随着内存页面数的增加,三种算法的访问命中率逐渐增大。在内存页面数为4~25个页面之间时,三种算法的命中率大致在56%至88%之间变化,但是,OPT算法和其他两种算法之间的差别一般在6~12个百分点左右。在内存页面为25~32个页面时,由于用户进程的所有指令基本上都已装入内存,从而命中率增加较大,各种算法之间的差别不大。 河南师范大学计算机与信息技术学院实验报告 比较上述三种算法,OPT算法的命中率最高,LRU算法和FIFO算法的命中率则较为接近。 六、总结 经过测试结果完全正常。经过编写和学习让我对操作系统方面的知识更深一步的得到了理解和巩固。让我在今后的学习中可以更好的去理解和体会。 操作系统课程设计 设计题目 动态分区分配存储管理 学生姓名学 号 专业班级 指导教师 吕 霆 20102675 计算机10-01班 第一章 课程设计概述 1.1 设计任务: 动态分区分配存储管理 1.2 设计要求 建立描述内存分配状况的数据结构; 建立描述进程的数据结构; 使用两种方式产生进程:(a)自动产生,(b)手工输入; 在屏幕上显示内存的分配状况、每个进程的执行情况; 建立分区的分配与回收算法,支持紧凑算法; 时间的流逝可用下面几种方法模拟:(a)按键盘,每按一次可认为过一个时间单位;(b)响应WM_TIMER; 将一批进程的执行情况存入磁盘文件,以后可以读出并重放; 支持算法:首次适应算法、循环首次适应算法、最佳适应算法:最坏适应算法。 1.3 设计目的 旨在让我们更好的了解动态分区管理方面的知识.第二章 原理及算法描述 2.1动态分区分配算法原理 首次适应算法 * 算法概述:分配内存时,从链首开始顺序查找,找到满足的空闲分区则划出空间分配,余下的空闲空间仍保留在空闲链表中 * 实现方法:分配时从数组第一个元素开始比较,若符合条件则将该元素减去对应作业的值 循环首次适应算法 * 算法概述:由首次适应算法演变,只是每次分配改为由上一次找到的空闲分区开始查找 * 实现方法:在首次适应算法的基础上增加一个值用于记录找到的空闲分区的位置 最佳适应算法 * 算法概述:每次为作业分配内存时,总是把能满足要求、又是最小的空闲分区 分配给作业 * 实现方法:我们决定每次分配先把空闲分区按从小到大的顺序排列,然后将第一个匹配分区分配给作业 最坏适应算法 * 算法概述:每次为作业分配内存时,总是挑选一个最大的空闲分区分割给作业使用 * 实现方法:算法与最佳适应算法几乎相同,仅在排序时把空闲分区表按从大到小的顺序排列,所以未作详细注释 回收分区 当进程运行完毕释放内存时,系统根据回收区的首址,从空闲区链(表)中找到相应的插入点,此时可能出现以下四种情况之一;1)回收区与插入点的前一个空闲分区F1相邻接,此时应将回收区与插入点的前一分区合并,不必为回收区分配新表项,而只需修改其前一分区F1的大小.2)回收分区与插入点的后一空闲分区F2相邻接,此时也可将两分区合并,形成新的空闲分区,但用回收区的首址作为新空闲区的首址,大小为两者之和.3)回收区同时与插入点的前,后两个分区邻接,此时将三个分区合并,使用F1的表项和F1的首址,取消F2的表项,大小为三者之和.4)回收区既不与F1相邻接,又不与F2邻接.这时应为回收区单独建立一新表项,填写回收区的首址和大小,并根据其首址插入到空闲链中的适当位置.紧凑算法 通过移动内存中的作业的位置,以把原来多个分散的小分区拼接成一个大分区的方法.第三章 开发环境 此程序是本人利用c++语言在vs2012的开发环境中实现的第四章 程序实现--数据结构 #include int recycle;//需要回收的盘块序号 int id1;//算法选择号 int m;//内存区数 int n;//空闲区数 int q;//进程数 int r=0;//循环首次适应算法:对应的这次查找到的空闲分区序号 //打印输出函数 void vision(){ int i;int j;if(id1==1)stream.open(“first_fit.txt”, ios::app);if(id1==2)stream.open(“nextfirst_fit.txt”, ios::app);if(id1==3)stream.open(“best_fit.txt”,ios::app);if(id1==4)stream.open(“worst_fit.txt”, ios::app);if(id1==5)stream.open(“compact.txt”,ios::app);if(id1==6)stream.open(“huishou.txt”,ios::app);cout<<“-------------内存分配状态-------------”< } cout < } cout < } //作业信息的自动产生 void create_pro(){ } //作业的手动生成 void create_zuoye(){ int j;int choice2;int id3=rand()%10;m=id3;//内存区数量 cout<<“产生”< } ary3[0]=42;ary3[1]=86;ary3[i]=rand()%100;if(ary3[i]==0){i--;} { } cout<<“--------------------------”< } //内存信息的自动产生 void create_apply(){ int k=0;//空闲区数量 for(i=0;i if(ary1[i][3]!=2){ary2[k][0]=ary1[i][0];ary2[k][1]=ary1[i][1];ary2[k][2]=ary1[i][2];k++;} int i;for(i=0;i } ary1[i][0]=i+1;ary1[i][1]=rand()%100;if(i==0){ } ary1[i][3]=rand()%3;//cout <>choice2;q=choice2;cout<<“输入想创建的作业请求大小”< } cout<<“你创建了”< } //内存信息的手动生成 int create_fenqu(){ } //首次适应算法 void first_fit()int k,x,y,o=0;int a=0;cout<<“输入想创建的内存分区块数 : ”;cin>>k; cout<<“输入”< } cout<<“输入内存块的分配状态”< } ary1[0][2]=0;ary1[1][2]=ary1[0][1];for(int i=2;i for(int i=0;i } n=a;return m,n;if(ary1[i][3]!=2){ } ary2[a][0]=ary1[i][0];ary2[a][1]=ary1[i][1];ary2[a][2]=ary1[i][2];a++;ary1[i][2]=ary1[i-1][2]+ary1[i-1][1];//起始地址 cin>>y;if(y==2){ } ary1[i][3]=y;//状态 n++;ary1[i][0]=i;//序号 cin>>x;ary1[i][1]=x;//大小 } n=k;//空闲块数量 { vision();int i;int j;int k;int l;int d;//用来保存第k个的值 int id2=0;for(i=0;i for(j=0;j if(ary2[j][1]>=ary3[i])//进程占用空间小于等于其中一个空闲区的大小 { cout<<“[”< ary1[ary2[j][0]-1][3]=2;for(k=j+1;k ary2[k-1][0]=ary2[k][0];ary2[k-1][1]=ary2[k][1];ary2[k-1][2]=ary2[k][2];} n--; }else//否则的话,空闲链对应的地方盘块大小小了进程占用的大小,并且内存分配从对应的 { l=ary2[j][0];d=ary1[l-1][1];//大小 ary1[l-1][1]=ary3[i];ary1[l-1][3]=2;m++;for(k=m;k>ary2[j][0]+1;k--){ ary1[k-1][0]=ary1[k-2][0]+1;ary1[k-1][1]=ary1[k-2][1];ary1[k-1][2]=ary1[k-2][2];ary1[k-1][3]=ary1[k-2][3];那一项开始增加一项 } l=ary2[j][0]; } } { if(ary1[id2][3]!=2) } } n=k;} break;} else { } cout<<“[”< { ary2[k][0]=ary1[id2][0];ary2[k][1]=ary1[id2][1];ary2[k][2]=ary1[id2][2];k++;} ary1[l][0]=l+1;ary1[l][1]=d-ary3[i];ary1[l][2]=ary1[l-1][1]+ary1[l-1][2];ary1[l][3]=0;k=0;for(id2=0;id2 //首次循环适应算法 void next_fit(){ vision();int i;int j;int k;int s;int d;int id2;for(i=0;i { for(j=r;j if(ary3[i]<=ary2[j][1]){ cout<<“[”< { } else//对应的空闲块大小大于进程需要大小 { //-----改变内存分配情况-----r=(r+1)%n;//改变第k块的内容 k=ary2[j][0];d=ary1[k-1][1];ary1[k-1][1]=ary3[i];ary1[k-1][3]=2;//从k+1之后所有向后移一格 m++;//内存块数增加1 for(s=m-1;s>k;s--){ ary1[s][0]=ary1[s-1][0]+1;ary1[s][1]=ary1[s-1][1];ary1[s][2]=ary1[s-1][2];//---改变内存分配---k=ary2[j][0];//得到对应空闲块对应内存块的序号 k--;ary1[k][3]=2;//把对应内存块标志位上改成已分配 //------------------//--改变空闲块表:把从这块空闲块以下的所有空闲块向上移一格--n--;for(k=j;k } vision();//------------------break;ary2[k][0]=ary2[k+1][0];ary2[k][1]=ary2[k+1][1];ary2[k][2]=ary2[k+1][2];stream<<“[”< } } //思路:先把空闲列表检索一遍,选出最佳答案,进行分配 void best_fit()//最佳算法--按顺序检索,把与进程要求内存大小最接近的快分配给进程 { int i;int s;int j=-9999;//用来保存最接近的答案 int e;//用来存放进行比较时的中间结果 } { if(ary1[id2][3]!=2) } } else{ } cout<<“[”< { ary2[k][0]=ary1[id2][0];ary2[k][1]=ary1[id2][1];ary2[k][2]=ary1[id2][2];k++;} ary1[s][3]=ary1[s-1][3];} //改变第k+1块内容:对应的数组是ary1[k] ary1[k][0]=ary1[k-1][0]+1;ary1[k][1]=d-ary1[k-1][1];ary1[k][2]=ary1[k-1][1]+ary1[k-1][2];//--------------------------//----改变空闲表分配情况----k=0;for(id2=0;id2 }else { cout<<“[”< for(s=0;s } if(j<0){ cout<<“[”< if(ary2[j][1]==ary3[i]){ } else for(l=k;l } n--;ary2[l-1][0]=ary2[l][0];ary2[l-1][1]=ary2[l][1];ary2[l-1][2]=ary2[l][2];ary1[k-1][3]=2;k=ary2[j][0]; } //最坏适应算法 void worst_fit() } { if(ary1[id2][3]!=2) } } vision();n=k; } for(k=j+1;k { ary2[k][0]=ary1[id2][0];ary2[k][1]=ary1[id2][1];ary2[k][2]=ary1[id2][2];k++;} { //把对应的内存分配进行更改 k=ary2[j][0];d=ary1[k-1][1];ary1[k-1][1]=ary3[i];ary1[k-1][3]=2;m++;for(l=m;l>ary2[j][0]+1;l--){ } k=ary2[j][0];ary1[k][0]=k+1;ary1[k][1]=d-ary1[k-1][1];ary1[k][2]=ary1[k-1][1]+ary1[k-1][2];ary1[k][3]=0;k=0;ary1[l-1][0]=ary1[l-2][0]+1;ary1[l-1][1]=ary1[l-2][1];ary1[l-1][2]=ary1[l-2][2];ary1[l-1][3]=ary1[l-2][3];for(id2=0;id2 { }else { cout<<“[”< int e=-9999;//用来存放进行比较时的中间结果 int k;int l;int d;int id2;vision();{ j=-9999;e=-9999;for(s=0;s } if(j<0){ cout<<“[”< if(ary2[j][1]==ary3[i]){ k=ary2[j][0]; ary1[k-1][3]=2; for(l=k;l { if(ary1[id2][3]!=2) } } vision();n=k; } for(k=j+1;k { ary2[k][0]=ary1[id2][0];ary2[k][1]=ary1[id2][1];ary2[k][2]=ary1[id2][2];k++;} } else { //把对应的内存分配进行更改 k=ary2[j][0];d=ary1[k-1][1];ary1[k-1][1]=ary3[i];ary1[k-1][3]=2;m++;for(l=m;l>ary2[j][0]+1;l--){ } k=ary2[j][0];ary1[k][0]=k+1;ary1[k][1]=d-ary1[k-1][1];ary1[k][2]=ary1[k-1][1]+ary1[k-1][2];ary1[k][3]=0;k=0;ary1[l-1][0]=ary1[l-2][0]+1;ary1[l-1][1]=ary1[l-2][1];ary1[l-1][2]=ary1[l-2][2];ary1[l-1][3]=ary1[l-2][3];} n--;ary2[l-1][2]=ary2[l][2];for(id2=0;id2 } //回收内存算法: /* 有共计八种情况,1.(1)回收区上邻接着空闲盘块,下连接着已分配盘块(2)回收区下邻接着空闲盘块,上邻接着已分配盘块(3)回收区上下连接的都是空闲盘块(4)空闲区上下邻接的都是已分配盘块 (5)要回收的盘块就是第一个盘块,并且向下邻接着空闲盘块(6)要回收的盘块就是第一个盘块,但是向下邻接着已分配盘块(7)要回收的盘块就是最后一个盘块,并且向上邻接的是空闲盘块(8)要回收的盘块就是最后一个盘块,但是向上邻接的是已分配盘块 */ void apply_recycle(){ ary1[0][1]=ary1[0][1]+ary1[1][1];ary1[0][3]=0;for(i=1;i if(recycle==1){ //cout< if(ary1[1][3]!=2){ cout<<“要回收的盘块就是第一个盘块,并且向下邻接着空闲盘块”< } else { ary1[0][3]=0;n++;ary2[0][0]=1;ary2[0][1]=ary1[0][1];ary2[0][2]=ary1[0][2];vision();} stream<<“要回收的盘块就是第一个盘块,并且向下邻接着空闲盘块”< ary2[k][0]=ary1[j][0]; ary1[0][3]=0;k=0;for(j=0;j //cout<<“ary1[j][3]”< } else{ cout<<“要回收的盘块就是第一个盘块,但是向下邻接着已分配盘块”< } n=k;vision(); } ary2[k][0]=ary1[j][0];ary2[k][1]=ary1[j][1];ary2[k][2]=ary1[j][2];k++; { } m--;// cout<<“" k=0;vision();//cout<<”ary1[0][3]“< cout<<”ary1[j][3]“< } else{ cout<<”要回收的盘块就是最后一个盘块,但是向上邻接的是已分配盘块“< } n=k;vision(); } ary2[k][0]=ary1[j][0];ary2[k][1]=ary1[j][1];ary2[k][2]=ary1[j][2];k++; ary1[recycle-2][3]=0;ary1[recycle-2][1]=ary1[recycle-2][1]+ary1[recycle-1][1];m--;k=0;for(j=0;j //cout<<”ary1[j][3]“< } else if(recycle==m){ if(ary1[recycle-2][3]!=2){ cout<<”要回收的盘块就是最后一个盘块,并且向上邻接的是空闲盘块“< } n=k;vision(); } ary2[k][1]=ary1[j][1];ary2[k][2]=ary1[j][2];k++;stream<<”要回收的盘块就是最后一个盘块,并且向上邻接的是空闲盘块“< ary2[k][0]=ary1[j][0];ary2[k][1]=ary1[j][1];ary2[k][2]=ary1[j][2];k++; } else{//剩下比较复杂的四种情况 if((ary1[recycle-2][3]!=2)&&(ary1[recycle][3]==2))//回收区上邻接着空闲盘块,下连接着{cout<<”回收区上邻接着空闲盘块,下连接着已分配盘块“< } } n=k;vision(); } ary2[k][0]=ary1[j][0];ary2[k][1]=ary1[j][1];ary2[k][2]=ary1[j][2];k++; ary1[recycle-1][3]=0;k=0;for(j=0;j //cout<<”ary1[j][3]“< stream<<”回收区上邻接着空闲盘块,下连接着已分配盘块“< ary1[recycle-2][1]=ary1[recycle-2][1]+ary1[recycle-1][1];for(i=recycle-1;i } m--;k=0;for(j=0;j //cout<<”ary1[j][3]“< } if((ary1[recycle-2][3]!=2)&&(ary1[recycle][3]!=2))//回收区上下连接的都是空闲盘块 { cout<<”回收区上下连接的都是空闲盘块“< } vision(); } ary2[k][0]=ary1[j][0];ary2[k][1]=ary1[j][1];ary2[k][2]=ary1[j][2];k++; } n=k;vision();} if((ary1[recycle][3]!=2)&&(ary1[recycle-2][3]==2))//回收区下邻接着空闲盘块,上邻接着{ cout<<”回收区下邻接着空闲盘块,上邻接着已分配盘块“< stream<<”回收区下邻接着空闲盘块,上邻接着已分配盘块“< ary1[recycle-2][3]=0;ary1[recycle-2][1]=ary1[recycle-2][1]+ary1[recycle-1][1];for(i=recycle-1;i } m--;k=0;for(j=0;j //cout<<”ary1[j][3]“< } } } if((ary1[recycle-2][3]==2)&&(ary1[recycle][3]==2))//空闲区上下邻接的都是已分配盘块 { } ary1[recycle-1][3]=0;k=0;for(j=0;j } vision();//cout<<”ary1[j][3]“< } ary2[k][0]=ary1[j][0];ary2[k][1]=ary1[j][1];ary2[k][2]=ary1[j][2];k++;cout<<”回收区上下连接的都是空闲盘块“< } m=m-2;k=0;for(j=0;j } vision();//cout<<”ary1[j][3]“< } ary2[k][0]=ary1[j][0];ary2[k][1]=ary1[j][1];ary2[k][2]=ary1[j][2];k++;ary1[recycle-1][0]=ary1[recycle+1][0]-2;ary1[recycle-1][1]=ary1[recycle+1][1];ary1[recycle-1][2]=ary1[recycle+1][2];ary1[recycle-1][3]=ary1[recycle+1][3];n=k;n=k; } //紧凑算法 void compact(){ num_avl=0;for(id2=0;id2 int num_avl;//记录空闲盘块数量 int sum_avl=0;//总共空闲区大小 int num_apl=0;//统计总共空闲区有多大 vision();for(id2=0;id2 } //最后一块空闲块 ary1[num_apl][0]=num_apl+1;ary1[num_apl][1]=sum_avl;ary1[num_apl][2]=ary1[num_apl-1][1]+ary1[num_apl-1][2];ary1[num_apl][3]=0;m=num_apl+1;//包括最后一个空闲区 if(ary1[id2][3]==2){ } ary1[num_apl][0]=num_apl+1;ary1[num_apl][1]=ary1[id2][1];if(num_apl==0){ } ary1[num_apl][3]=2;num_apl++;//cout<<”num_apl“< } //主函数入口 void main(){ if(choice1==1){ } num=rand()&10;q=num;int id3=2+rand()%8;m=id3;//内存区数量 create_apply();create_pro();int i;int j;int num;int choice1;//操作选择标记 int choice2;int flag=1;//标记是否再执行 while(flag==1){ cout<<”********************************************“< } n=num_avl;vision(); } ary2[num_avl][0]=ary1[id2][0];ary2[num_avl][1]=ary1[id2][1];ary2[num_avl][2]=ary1[id2][2];num_avl++;if(choice1==2){ } vision(); cout<<”**------------------请选择处理算法----------------------**“< } } cout<<”**************************** “< cout<<”**1首次适应算法-----2循环首次适应算法-----3最佳适应算法 **“< } {first_fit();} {next_fit();} {best_fit();} {worst_fit();} { compact();} if(id1==6){ cout<<”*******************生成内存状态******************“< } cout<<”错误:内存中不存在此块!“< } if(id2==-9999){ apply_recycle();} if(ary2[i][0]==recycle){ } cout<<”错误:输入的为空闲盘块!"< 操作系统实验 实验一 Linux常用命令实验 一.目的和要求 本实验的目的是熟悉Linux操作系统的命令接口、图形接口和程序接口;了解Linux操作系统的启动过程;了解Linux操作系统的目录结构;用vi编辑器编写简单的C语言程序,并用gcc编译器编译、运行。 二.实验内容 1、实现开机、登录、退出与关机: (1)如果以root用户登录,则命令窗口的提示符为#;如果以普通用户登录,则命令窗口的提示符为$;登陆用户名:user 密码:123456(2)修改口令(修改口令操作不做):成功进入系统后,在命令提示符后输入“passwd”并键入回车键 (3)退出帐号:命令方式下:logout(4)关机或重启: 命令方式下:halt或reboot 窗口方式下:“桌面”->“注销” 2、掌握的基本常用命令列表 (1)关于目录的操作命令:cd、ls、mkdir、rmdir、pwd等; (2)关于文件的操作命令:cat、find、man/help、vi/vim、cp、rm、mv、dd、du、df、chmod、ln等; (3)关于进程管理的操作命令:ps、kill、top、free 等; (4)关于系统管理的操作命令:whoami、passwd、adduser/useradd、addgroup、userdel、groupdel、su、who、Ctrl+Alt+Fn(n=1、2、3、4、5、6)(在X-Window界面下切换到字符界面,重新登录,Ctrl+Alt+F7返回图形界面)、Alt+Fn(n=1、2、3、4、5、6)(在6个虚拟终端之间切换)等; (5)安装和卸载文件系统:mount、umount等; (6)显示有关计算机系统信息的命令:uname(显示操作系统的名称)、uname –n(显示系统域名)、uname –p(显示系统的CPU名称) (7)其它命令:time、date、cal 等。 3、阅读/etc/inittab 文本文件,思考问题:如果要求启动Linux系统之后进入字符 1 操作系统实验 界面,应如何修改/etc/inittab文件?用户应具有什么权限? 4、切换到不同的虚拟终端,登录到Linux系统 5、vi 编辑器的使用(1)进入和退出vi(2)利用文本插入方式建立一个文件(3)在新建的文本文件上移动光标。 (4)对文本文件执行删除、复原、修改、替换操作。 6、熟悉gcc编译环境:编写一个C语言程序myfile1.c,求1~100中偶数的和,编译并运行。 (1)编译 gcc myfile1.c 运行./a.out(2)编译 gcc –o myfile1 myfile1.c 运行./myfile1 7、编写一个C语言程序myfile2.c,显示字符串“Hello, Linux!”,并将其反向输出。 8、熟悉Linux系统的目录结构,使用命令或者编写C语言程序报告Linux内核的行为。 报告以下内容: CPU类型和型号 内核版本 从系统最后一次启动以来经历了多长时间?形式为dd:hh:mm:ss 当前配置的内存数量 当前可用内存数量 自系统启动以来,发生的所有的中断的次数 从系统启动开始创建的进程数 内核执行的上下文转换的次数 三.实验提示 1、Linux安装 (1)安装前的准备工作 <1>.基本的硬件配置 由于安装涉及到各种硬件的设置,所以在安装前必须了解机器各种硬件的型号,硬盘的使用情况,内存的大小,鼠标的类型及接口,声卡,网卡,显卡,显示器的型号。 操作系统实验 <2>.有关网络的信息 IP地址,掩码,网关IP地址,域名服务器IP地址,域名,宿主机名。<3>.安装方式的选择 •从CD-ROM安装 •从FTP站点安装 •从NFS服务器安装 •从硬盘安装 硬盘分区 硬盘空间必须和计算机上安装的其他操作系统所使用的硬盘空间分开。特别要注意,如果硬盘空间很大,切忌不能将Linux装在8G以后。安装Red Hat Linux至少需要两个硬盘分区:一个或多个“Linux native”类型的分区,一个“Linux swap”类型的分区 分区命名设计Linux 通过字母和数字的组合来表示硬盘分区。 前两个字母-----分区名的前两个字母表明分区所在设备的类型。hd指IDE硬盘,sd指SCSI硬盘。 下一个字母-----分区在哪个设备。例如,/dev/hda(第一个IDE硬盘),/dev/sdb(第二个SCSI硬盘)。 数字-----代表分区。前四个分区(主分区或扩展分区)用数字1到4表示。逻辑分区从5开始。例如, 若IDE硬盘在安装Linux前安装了Windows系统并划分了C盘和逻辑分区D盘,那么D盘就是/dev/hda5, /dev/hda5表示第一个硬盘的第一个逻辑分区。 对于Linux初学者来说,为Linux分两个区(根分区和交换分区)是比较简单方便的。 一个交换分区:用来支持虚拟内存。一个根分区:根分区是/(根目录)的所在地,其中包含启动系统所需的文件和系统配置文件。这个分区要足够大。 一个/usr分区: /usr是Linux系统许多软件所在的地方。一个/home分区:这是用户的主目录所在地。(2)开始安装 注意点:我们一般选择的是图形化的安装方式。它的主要部分是相同的。 可能会在安装完成后第一次启动时才进行网卡的检测。 操作系统实验 在选择图形化界面时,有两种方式gnome和kde;它们各有优缺点。 系统会让你选择启动时是图形化方式,还是字符方式。请大家选择字符方式。 在选择防火墙的时候,在安装时请先不用防火墙。 图形化安装方式下,不能选择启动时的开启服务。可在系统安装完成后用setup命令进行修改。 2、进入Linux(1)登录 第一次登录系统,必须作为“root”登录。这个帐号对系统的一切都有完全的访问权限。 在login:提示符处输入root。按[Enter](或[Return]键).会出现Password提示。输入口令,应该看到类似以下的信息: [root@localhost /root] #(2)退出 输入[Ctrl]-[D](3)帐号和口令 <1>.帐号 创建新的帐号有几种方法,最基本的方法:useradd命令.[root @ localhost / root] # useradd Tom [root @ localhost / root] # <2>.口令 passwd 命令可以用来: 为新创建的用户分配口令。 修改已存在的用户的口令。 修改登录的用户的口令。此时必须以root登录。如: [root @ localhost / root]# passwd Tom New UNIX password: Retype new UNIX password: passwd:all authentication tokens updated successfully 4 操作系统实验 [root @ localhost / root]# 用新帐户登录: Red Hat Linux release 7.1(Manhattan) Kernel 2.0.34 on an i586 login: Tom Password: [Tom@ localhost Tom] $ <3>.su 命令 用su,当前的登录段能变成root(或其他用户)的登录段。如: [Tom@ localhost Tom] $ su Password: [root@ localhost Tom] # 也可以用su变成其他用户。这时,必须作为root运行su,给出用户名。<4>.关闭系统 关闭系统时,必须告诉所有的进程结束运行,使用shutdown命令。且只能由root 运行,格式是: shutdown -h-------在关闭完成后(Halt)停止系统。 -r--------在关闭完成后重启动(Reboot)系统。 3、vi 编辑器的使用(1)进入和退出vi <1>进入vi 在系统提示符($)下输入命令vi和想要编辑(建立)的文件名(如example),便可进入vi。 <2>退出vi 在命令方式下可有几种方法退出vi编辑器: :wq 把编辑缓冲区的内容写到正在编辑的文件中,退出编辑器,回到Linux shell下。 :ZZ 仅当作过修改时才将缓冲区内容写到文件上。 操作系统实验 :x 与 :ZZ 相同。 :q!强行退出vi。感叹号(!)告诉vi,无条件退出,丢弃缓冲区内容。这样,先前对该文件所做的修改或输入都被抛弃。(2)新建文件 <1>在Linux提示符$之后,输入命令 :vi myfile,然后按〈Enter〉键。<2>输入插入命令i(屏幕上看不到字符i)。<3>然后,输入以下文本行: To the only book tht I, For mang year you have been my favourite book <4>发现这两行有错,进行改正: 按〈Esc〉键,从插入方式回到命令方式。按光标上移键,使光标移到第一行。 按光标左移键,使光标移到“tht”的第二个“t”处。 输入i(这是插入命令),然后输入a。该行变成如下形式: To the only book that I, 按光标右移键,使光标移到“I”上。 我们想在“I”之后输入一个空格和单词“like”。为此,输入附加命令“a”。结果屏幕显示为: To the only book that a I,没有出现预期的效果......原来是:我们先前使用了插入命令i,至今并未用〈Esc〉键返回命令方式。所以,输入的所有字符都作为输入文本予以显示。<5>按〈Esc〉键,返回命令方式。 利用x命令删除错误字符。然后,进入插入方式,输入正确字符。<6>最后输入如下所示的文本: To the only book that I like, For many year you have been my favourite book I liveeyou all the time and could not have picked much better.<7>将编辑的文本文件存盘。(利用“:wq”命令,或者“:x”命令)<8>重新进入vi编辑程序,编辑上面的文件。(如:$ vi myfile) 操作系统实验 <9>在屏幕上见到myfile文件的内容。在屏幕底边一行显示出该文件的名称、行数和字符个数:“myfile”4 lines,130 characters 它仍然有错,需进一步修改。 <10>将光标移到第二行的year的r处。输入a命令,添加字符s。 <11>按〈Esc〉,回到命令方式。输入命令10〈Space〉,光标移至何处?---光标右移10个字符位置。 <12>利用取代命令r将liveeyou改为live you。 <13>将光标移至第三行。输入新行命令O(大写字母),屏幕上有什么变化?---光标移至上一行(新加空行)的开头。<14>输入新行的内容: We've been through much together 此时,vi处于哪种工作方式? <15>按〈Esc〉,回到命令方式。将光标移到第四行的live的v字母处。利用替换命令s将v改为k。 <16>在第四行的you之后添加单词very much。<17>修改后的文本是以下内容: To the only book that I like, For many years you have been my favourite book We've been through much together I like you very much all the the time and could not have picked much better.将该文件存盘,退出vi。 <18>重新编辑该文件。并将光标移到最后一行的have的v字母处,使用d$命令将v至行尾的字符都删除。 <19>现在想恢复17步的原状,怎么办?(使用复原命令u) <20>使用dd命令删除第一行;将光标移至through的u字母处,使用C(大写字母)命令进行修改,随便输入一串字符。将光标移到下一行的开头,执行5x命令;然后执行重复命令(.)。 <21>屏幕内容乱了!现在想恢复17步的原状,怎么办?(不写盘,强行退出vi) 4、Linux内核 操作系统实验 Linux 内核源程序目录结构(/usr/src/redhat/SOURCES)如下: /document :保存帮助文档 /arch :包含多个子目录,每个存放与特定体系结构相关的代码。如arch/i386(intel 386 体系结构),arch/sparc,arch/alpha等。每个子目录下至少又包含三个子目录: kernel(存放支持该体系结构特有的诸如信号处理和SMP之类特征的实现); lib(存放该体系结构特有的诸如Strlen和memcpy之类的高效率函数); mm(存放该体系结构特有的诸如内存管理程序的实现) /drivers :该目录占内核代码一半以上,包括显卡、网卡、SCSI适配器、软驱、PCI设备和其他外设的软件驱动程序。/fs:包含linux支持的文件系统。 /include :包含源程序中大部分包含(.h)文件。/init: 包含main.c,保存大部分协调内核初始化的代码。/ipc:实现了SYSTEM V的进程间通讯IPC。 /kernel:包含了linux最重要的部分:实现平台独立的基本功能,包括Sched.c、fork.c、exit.c。 /lib :存放字符串和内存操作函数。 /mm:包含与体系结构无关的内存管理代码。/net:包含了linux应用的网络协议代码。/script :包含用来配置内核的脚本。 5、报告Linux状态(/proc 中的信息) 在终端窗口提示符下,可以使用cat命令显示相关文件的内容,如: cat /proc/cpuinfo 通过编写程序,显示相关文件内容:应用文件操作,将相关 /proc中的文件读入到缓冲区中,可用fgets()函数按行取文件中数据,通过strstr()检验包含所需数据字符串。如存在,用printf()函数输出。(1)CPU类型和型号 /proc/cpuinfo文件提供了有关CPU的多种信息,这些信息是从内核里对CPU的测试代码中得到的。文件列出了CPU个数:processor;CPU制造商:vendor_id;CPU架构:model;CPU名称:model name;CPU时钟频率:cpu MHz;CPU缓存大小: 8 操作系统实验 cache size;CPU包含的指令集:flags。文件还包含了以bogomips表示的处理机速度,而且如果检测到CPU的多种特性或bug,文件还会包含相应的标志。该文件的格式为:文件由多行构成,每行包括一个域名称、一个冒号和一个值。 通过fopen()函数打开包含CPU类型和型号的文件cpuinfo,把内容读入字符数组char_all,然后通过strstr()函数查找CPU类型和型号所在的位置,用strncpy()函数拷贝到字符数组中,通过printf()标准输出函数输出。(2)存储器信息 /proc/meminfo 文件给出了内存状态的信息。它显示出系统中物理内存的总量:MenTotal;未使用的物理内存的总量:MemFree;用做文件缓冲的物理内存的总量:buffers;用做缓冲的物理内存的总量:Cached;活跃的内存大小:Active;不活跃的内存大小:Inactive;交换分区的总量:SwapTotal;交换分区未使用的总量:SwapFree等信息。(3)内核版本 文件/proc/version显示了正在运行的内核版本、编译此内核的gcc版本以及该内核的编译时间。 (4)从系统最后一次启动以来的时间,形式为dd:hh:mm:ss uptime读出的时间是以秒计的,所以根据要求要转换为天:小时:分钟:秒。1天为86400秒,1小时为3600秒,1分钟为60秒。通过两个运算符就可以很好的转换:“/”做除法取整运算,“%”做除法取余运算。举例:86800秒,(86800/86400)=1(天),(86800%86400)=400(余400秒);400秒,(400/3600)=0小时,(400%3600)=400(余400秒);400秒,(400/60)=6分钟,(400%60)=40(余40秒)。所以最后结果为:1:0:6:40。(5)其他信息的读取 从/proc/stat中读取信息 CPU花费在用户态、系统态和空闲态的时间——cpu 自系统启动以来,发生的所有的中断的次数——intr 内核执行的上下文转换的次数----ctxt 系统最后启动的时间----btime 从系统启动开始创建的进程数----processes 6、Linux的目录结构 操作系统实验 对于Linux来讲它的树型结构与Windows不同,Windows可以有多个分区,每个分区都有根,但Linux 只有一个根,其他的所有文件、目录或硬盘分区、软盘、光盘、U 盘都必须mount(挂载)到Linux 根下的一个目录中才能被访问和使用。下面列出根目录下的常见系统目录及其用途。 /bin bin是binary的缩写。这个目录沿袭了UNIX系统的结构,存放着使用者最经常使用的命令。例如cp、ls、cat,等等。 /boot 这里存放的是启动Linux时使用的一些核心文件。 /dev dev是device(设备)的缩写。这个目录下是所有Linux的外部设备,其功能类似DOS下的.sys和Win下的.vxd。在Linux中设备和文件是用同种方法访问的。例如:/dev/hda代表第一个物理IDE硬盘。 /etc 这个目录用来存放系统管理所需要的配置文件(例如配置文件inittab)和子目录。 /home 用户的主目录,比如说有个用户叫wang,那他的主目录就是/home/wang,也可以用~wang表示。 /lib 这个目录里存放着系统最基本的动态链接共享库,其作用类似于Windows里的.dll文件。几乎所有的应用程序都需要用到这些共享库。 /lost+found 这个目录平时是空的,当系统不正常关机后,这里就成了一些无家可归的文件的避难所,有点类似于DOS下的.chk文件。 /media 用来挂载光盘、U盘等文件系统的目录。/misc 用来挂载NFS 共享目录。 /mnt 用于挂载其他硬盘分区系统的目录(如挂载xp分区)。 /opt 某些第三方软件商软件的安装地点,如国产红旗office就存放于此。/proc 这个目录是一个虚拟的目录,它是系统内存的映射,可以通过直接访问这个目录来获取系统信息。也就是说,这个目录的内容不在硬盘上而是在内存里。 /root 系统管理员(也叫超级用户)的主目录。作为系统的拥有者,总要有些特权,比如单独拥有一个目录。 /sbin s就是Super User的意思,也就是说这里存放的是系统管理员使用的管理程序。 /tmp 这个目录是用来存放一些临时文件的地方。 /usr 这是最庞大的目录,要用到的应用程序和文件几乎都存放在这个目录 10 操作系统实验 下。其中包含以下子目录: /usr/X11R6 存放X-Window的目录; /usr/bin 存放着许多应用程序; /usr/sbin 给超级用户使用的一些管理程序就放在这里; /usr/include Linux下开发和编译应用程序需要的头文件,在这里查找; /usr/lib 存放一些常用的动态链接共享库和静态档案库; /usr/local 这是提供给一般用户的/usr目录,在这里安装软件最适合; /usr/src Linux开放的源代码就存在这个目录。 /var 这个目录中存放着那些不断在扩充着的东西,为了保持usr的相对稳定,那些经常被修改的目录可以放在这个目录下,实际上许多系统管理员都是这样做的。另外,系统的日志文件就在/var/log目录中。 我们一般日常能经常访问的目录有/home 目录、/mnt目录、/media 目录、/usr 目录。 计算机操作系统实验 存储管理模拟 实验三 存储管理模拟 一、内容补充说明 实验中固定内存分区采用结构体数组实现,分区信息采用结构体初始化实现。由于结构体中含string类,VC++不能对含string类的结构体初始化,这里采用MinGW编译器作为程序平台。 MinGW,即 Minimalist GNU For Windows。它是一些头文件和端口库的集合,该集合允许人们在没有第三方动态链接库的情况下使用 GCC(GNU Compiler C)产生 Windows32 程序。实际上 MinGW 并不是一个 C/C++ 编译器,而是一套 GNU 工具集合。除开 GCC(GNU 编译器集合)以外,MinGW 还包含有一些其他的 GNU 程序开发工具(比如 gawk bison 等等)。开发 MinGW 是为了那些不喜欢工作在 Linux(FreeBSD)操作系统而留在 Windows 的人提供一套符合 GNU 的 GNU 工作环境。 内存的分配采用“最先适应算法”实现,由于是固定分区,采用该算法不仅简单,而且保证效率。 二、分析和设计 1.理论分析 初始状态内存中没有作业运行;以后每5 秒钟随机生成一个作业,如果不能满足作业需求(主存中没有分区能够容纳的下),则丢弃该作业。作业以秒为单位进行驱动,当一个作业运行时间结束后将释放内存空间。 2.总体设计 分区信息采用初始化实现。 作业以秒为单位进行驱动。 作业的生成、内存的释放采用永真循环实现,当作业全部运行完后跳出循环。 新作业的大小和运行时间由随机数函数rand生成。 结构体Job用于生成新作业时用;结构体District是内存分区,作业分配成功后,作业的信息全部保存在District内,不再由专门的作业队列记录。 作业运行时间由District[i].Remainder记录,当每秒驱动一次时,时间将减少一秒。当时间减少为0时,作业运行结束,释放内存空间。 每分配一个作业,变量Jump自加一次;每释放一个作业时Jump自减一次。因为作业先分配、内存后释放,所以当作业都运行完时Jump必定等于初始值,这时程序跳出永真循环。 三、详细实现 for(int j=0,clock=0,flag=0;1;clock++,flag=0,Sleep(1000)) //总循环,永真循环,flag为作业分配标志,clock为作业产生时钟,Sleep为程序步进驱动 {if(j //每5秒产生一个新作业 {p=new Job; //产生新作业 …… for(int k=0;k<10;k++) if(District_table[k].Job_size==0 && p->Job_size<=District_table[k].District_size) //如果分区未分配并且作业大小小于分区大小 {District_table[k].Job_num=j; //分配内存,并将作业信息保存至分区信息中 …… for(int k=0;k<10;k++) if(District_table[k].Job_size!=0) //如果分区中有作业运行 District_table[k].Remainder-=1; //程序步进驱动,作业运行减1 …… for(int t=0;t<10;t++)if(District_table[t].Job_size!=0 && District_table[t].Remainder==0) //如果分区中有作业运行,并且作业已运行完成 计算机操作系统实验 存储管理模拟 {District_table[t].Job_size=0; //释放内存 …… 四、操作界面 操作界面为命令提示符界面,实验截图如下: 1.编译、连接界面 计算机操作系统实验 存储管理模拟 五、心得体会 程序最后跳出永真循环是用break语句。原来是用goto语句,用break语句或exit语句应该也是可以的,但运行后死活都跳不出来,后来不知道为什么又好了。 MinGW是一个很好的编译器,安装包小、程序文件小、绿色环保、速度快、效率高。另外,还有很多人性化的设计,比如:现实代码行数、网格显示、配对括号激活时高亮显示方便检查、括号代码折叠方便查看上下段程序等等。可惜Windows Vista用户没福气,幸好我是Windows XP。 六、附录 #include using namespace std; 计算机操作系统实验 存储管理模拟 cout<<“/\/\/\/分区表初始化完成\/\/\/\”< //打印内存分区内容 cout< //总循环,永真循环,flag为作业分配标志,clock为作业产生时钟,Sleep为程序步进驱动 {if(j //每5秒产生一个新作业 {p=new Job; //产生新作业 p->Job_size=rand()%34+1; //作业大小随机 p->Job_time=rand()%40+1; //作业运行时间随机 cout< Job_time<<“S”< for(int k=0;k<10;k++) if(District_table[k].Job_size==0 && p->Job_size<=District_table[k].District_size) //如果分区未分配并且作业大小小于分区大小 {District_table[k].Job_num=j; //分配内存,并将作业信息保存至分区信息中 District_table[k].Job_size=p->Job_size; District_table[k].Remainder=p->Job_time; District_table[k].District_state=“Allocated”; cout<<“↓↓↓↓↓↓↓↓↓↓分配成功,分配信息如下↓↓↓↓↓↓↓↓↓↓”< cout<<“分区号码 分区大小 作业大小 分区状态”< for(int t=0;t<10;t++) cout< flag=1; //分配成功 Jump+=1; //内存中作业数 break; } if(flag==0) //如果作业分配不成功 cout<<“→→→→→→→→→空间不足,分配失败,已丢弃→→→→→→→→→”< j++; //生成作业数 } for(int k=0;k<10;k++) if(District_table[k].Job_size!=0) //如果分区中有作业运行 District_table[k].Remainder-=1; //程序步进驱动,作业运行减1 for(int t=0;t<10;t++) if(District_table[t].Job_size!=0 && District_table[t].Remainder==0) //如果分区中有作业运行,并且作业已运行完成 {District_table[t].Job_size=0; //释放内存 District_table[t].District_state=“Unallocated”; cout< District_table[t].Job_num=0; cout<<“分区号码 分区大小 作业大小 分区状态”< for(int r=0;r<10;r++) cout< Jump-=1; //内存中作业数 操作系统实验总结 学号: 姓名: 班级: 在本学期的计算机操作系统这门课学习当中,为了更好的了解操作系统相关知识,我们通过OS Lab平台做了几个实验。在实验室的过程中,我对课堂上学到的操作系统的一些知识有了新的认识,同时还接触到了操作系统的相关源代码,而且通过实验的运行效果了解了平时我们看不到的操作系统的一些状况,收获还是很大的。下面先简要归纳在实验课上我做的几个实验的主要实验内容和实验步骤: 实验一:实验环境的使用 实验步骤: 1.1启动OS Lab OS Lab每次启动后都会首先弹出一个用于注册用户信息的对话框(可以选择对话框标题栏上的“帮助”按钮获得关于此对话框的帮助信息)。在此对话框中填入学号和姓名后,点击“确定”按钮完成本次注册。观察OS Lab主窗口的布局。OS Lab主要由下面的若干元素组成:菜单栏、工具栏以及停靠在左侧和底部的各种工具窗口,余下的区域用来放置编辑器窗口。 1.2 学习OS Lab的基本使用方法 练习使用OS Lab编写一个Windows控制台应用程序,熟悉OS Lab的基本使用方法(主要包括新建项目、生成项目、调试项目等)。 实验二:操作系统的启动 实验步骤: 2.1 准备实验 启动OS Lab,新建一个EOS Kernel项目,在“项目管理器”窗口中打开boot文件夹中的boot.asm和loader.asm两个汇编文件,按F7生成项目,生成完成后,使用Windows资源管理器打开项目文件夹中的Debug文件夹。找到由boot.asm生成的软盘引导扇区程序boot.bin文件,找到由loader.asm生成的loader程序loader.bin文件,记录下此文件的大小1566字节。 2.2 调试EOS操作系统的启动过程 2.2.1 使用Bochs做为远程目标机 将调试时使用的远程目标机修改为Bochs 2.2.2 调试BIOS程序 按F5启动调试,Bochs在CPU要执行的第一条指令(即BIOS的第一条指令)处中断,从Console窗口显示的内容中,我们可以获得关于BIOS第一条指令的相关信息,然后查看CPU在没有执行任何指令之前主要寄存器中的数据,以及内存中的数据。 2.2.3 调试软盘引导扇区程序 练习从0x7c00处调试软盘引导扇区程序;查看boot.lst文件;调试过程——软盘引导扇区程序的主要任务就是将软盘中的loader.bin文件加载到物理内存的0x1000处,然后跳转到loader程序的第一条指令(物理地址0x1000处的指令)继续执行loader程序; 2.2.4 调试加载程序 调试过程——Loader程序的主要任务是将操作系统内核(kernel.dll文件)加载到内存中,然后让CPU进入保护模式并且启用分页机制,最后进入操作系统内核开始执行(跳转到kernel.dll的入口点执行); 2.2.5 调试内核 2.2.6 EOS启动后的状态和行为 查看EOS的版本号;查看EOS启动后的进程和线程的信息;查看有应用程序运行时进程和线程的信息 实验三:进程的创建 实验步骤: 3.1 准备实验 启动OS Lab;新建一个EOS Kernel项目;分别使用Debug配置和Release配置生成此项目,从而在该项目文件夹中生成完全版本的EOS SDK文件夹;新建一个EOS应用程序项目;使用在第3步生成的SDK文件夹覆盖EOS应用程序项目文件夹中的SDK文件夹 3.2 练习使用控制台命令创建EOS应用程序的进程 3.3 练习通过编程的方式让应用程序创建另一个应用程序的进程 使用OS Lab打开本实验文件夹中的NewProc.c文件;查看应用程序创建另一个应用程序的进程的执行结果。 3.4 调试CreateProcess函数 调试CreateProcess函数创建进程的过程;分别验证应用程序和操作系统内核在进程的4G虚拟地址空间中所处的位置; 3.5 调试PsCreateProcess函数 调试PspCreateProcessEnvironment函数;调试进程控制块的创建过程;调试初始化进程控制块中各个成员变量的过程。 3.6 练习通过编程的方式创建应用程序的多个进程 使用OS Lab打开本实验文件夹中的参考源代码文件NewTwoProc.c,仔细阅读此文件中的源代码。使用NewTwoProc.c文件中的源代码替换EOS应用程序项目中EOSApp.c文件内的源代码,生成后启动调试,查看多个进程并发执行的结果。 实验四:线程的状态和转换 实验步骤: 4.1 准备实验 启动OS Lab,新建一个EOS Kernel项目 4.2 调试线程状态的转换过程 查看一下loop命令执行的效果;调试线程状态转换的过程;对断点进行一些调整。 4.2.1 线程由阻塞状态进入就绪状态: 将线程从等待队列中移除;将线程的状态由Waiting修改为Zero;将线程插入其优先级对应的就绪队列的队尾;将线程的状态由Zero修改为Ready。 4.2.2 线程由运行状态进入就绪状态: 线程中断运行,将线程中断运行时的上下文保存到线程控制块中;如果处于运行状态的线程被更高优先级的线程抢先,就需要将该线程插入其优先级对应的就绪队列的队首。(注意,如果处于运行状态的线程主动让出处理器,例如时间片用完,就需要将程插入其优先级对应的就绪队列的队尾);将线程的状态由Running修改为Ready 4.2.3 线程由就绪状态进入运行状态: 将线程从其优先级对应的就绪队列中移除;将线程的状态由Ready修改为Zero;将线程的状态由Zero修改为Running;将线程的上下文从线程控制块(TCB)复制到处理器的各个寄存器中,让线程从上次停止运行的位置继续运行。 4.2.4 线程由运行状态进入阻塞状态: 将线程插入等待队列的队尾;将线程的状态由Running修改为Waiting;将线程中断执行,并将处理器上下文保存到该线程的线程控制块中。 4.3 为线程增加挂起状态 观察loop线程被挂起的情况:删除之前添加的所有断点;按F5启动调试;待EOS启动完 毕,在EOS控制台中输入命令“loop”后按回车。此时可以看到loop线程的执行计数在不停增长,说明loop线程正在执行,记录下loop线程的ID;按Ctrl+F2切换到控制台2,输入命令“suspend 31”(如果loop线程的ID是31)后按回车;按Ctrl+1切换回控制台1,可以看到由于loop线程已经成功被挂起,其执行计数已经停止增长了。 在PsResumThread函数第119行需要添加的代码的流程可以是:首先调用List Remove Entry函数将线程从挂起线程队列中移除,然后调用PspReadyThread函数将线程恢复为就绪状态,最后调用PspThreadSchedule宏函数执行线程调度,让刚刚恢复的线程有机会执行。 实验过程: 做实验时,最开始并不是很了解OS Lab平台的使用,即使对着老师给的实验教程做还是不怎么会,于是请教会做的同学,通过同学的讲解我知道了怎样在OS Lab平台上建立项目,怎样更改路径并找到项目的源文件等等基本操作。 掌握对平台的简单应用后,做后面的实验我是按照实验教程上的步骤一步步的实施,并且每次都认真观察相应的运行结果,每个实验都会建议我们学习实验教程前面的理论部分,我想如果对他的理论不熟悉,就算试验成功了我也不知道为什么,所以我一般在做实验前会对前面的理论部分进行简要的学习和熟悉。做实验的过程中,有时候按照实验教程上的步骤做平台还是会出现一些错误,比如做实验三到调试CreateProcess函数时,出现的调试异常对话框中,本来是要点击“是”的,但做到这里电脑总是会出现像死机一样的状况,关掉平台重做到这里老是出现同样的问题,最后换电脑也是这样,然后我尝试不按照实验步骤点击“是”也不行,最后还是又还了电脑才做成功,问其他同学也有出现同样的问题,我想可能是平台和电脑上有什么地方有冲突吧。 之后做试验是遇到问题我还是选择多问同学,毕竟每个人擅长的是不同的,有些问题这个同学会解决,有些问题则是那个同学才懂解决,通过互相交流和学习,我们通过实验不仅巩固了课堂上学到的相关知识,也对操作系统有了更深的了解。 体会: 其实做完实验我还是不能保证我对OS Lab这个平台有很好的全面的了解,但是对一些基本操作及其快捷键我算是大致掌握了,通过这个平台我也是认识到了“没有做不到的,只有想不到的”,我觉得创建这个平台的人们真的是很了不起,这个平台让我们便动手便了解了平时我们看不到的操作系统的相关知识。要做好实验,得按照实验教程上面的内容一步步落实,要边做变领悟相关原理及运行结果的出现的原因,这样我们才能在试验中学到更多、掌握更多。其次,也遇到问题我们自然是要先自己思考,通过不同的尝试来解决,之后不能解决的我们要多向老师同学请教,通过互相交流得来的知识也是会让我们难忘的。第二篇:操作系统课程设计_动态分区分配存储管理
第三篇:操作系统实验
第四篇:实验3存储管理模拟
第五篇:操作系统实验总结