1、操作系统存储管理实验报告北京邮电大学 操作系统实验 实验报告实验日期:2010-12-20 实验名称:存储管理一、实验目的通过模拟实现内存分配的伙伴算法和请求页式存储管理的几种基本页面置换算法,了解存储技术的特点。掌握虚拟存储请求页式存储管理中几种基本页面置换算法的基本思想和实现过程,并比较它们的效率。二、实验内容实现一个内存管理的伙伴算法,实现内存块申请时的分配和释放后的回收。设计一个虚拟存储区和内存工作区,并使用下述算法计算访问命中率。1) 最佳置换算法(Optimal)2) 先进先出法(Fisrt In First Out)3) 最近最久未使用(Least Recently Used)4
2、) 最不经常使用法(Least Frequently Used)5) 最近未使用法(No Used Recently)其中,命中率页面失效次数页地址流长度。试对上述算法的性能加以较各:页面个数和命中率间的关系;同样情况下的命中率比较。三、实验分析对于伙伴算法,用随机函数仿真进程进行内存申请,并且以较为随机的次序进行释放。对其碎片进行统计,当申请分配内存失败时区分实际空间不足和由于碎片而不能满足。对于虚拟存储区和内存工作区的不同算法,其中主要的流程:首先用srand( )和rand( )函数定义和产生指令序列,然后将指令序列变换成相应的页地址流,并针对不同的算法计算出相应的命中率。实验可先从一个
3、具体的例子出发。(1)通过随机数产生一个指令序列,共320条指令。指令的地址按下述原则生成:A:50%的指令是顺序执行的B:25%的指令是均匀分布在前地址部分C:25%的指令是均匀分布在后地址部分具体的实施方法是:A:在0,319的指令地址之间随机选取一起点mB:顺序执行一条指令,即执行地址为m+1的指令C:在前地址0,m+1中随机选取一条指令并执行,该指令的地址为mD:顺序执行一条指令,其地址为m+1E:在后地址m+2,319中随机选取一条指令并执行F:重复步骤A-E,直到320次指令(2)将指令序列变换为页地址流设:页面大小为1K;用户内存容量4页到32页;用户虚存容量为32K。在用户虚存
4、中,按每K存放10条指令排列虚存地址,即320条指令在虚存中的存放方式为:第 0 条-第 9 条指令为第0页(对应虚存地址为0,9)第10条-第19条指令为第1页(对应虚存地址为10,19)第310条-第319条指令为第31页(对应虚存地址为310,319)按以上方式,用户指令可组成32页。四、编程实现伙伴算法原理:伙伴算法把所有的空闲页面分为10个块组,每组中块的大小是2的幂次方个页面,例如,第0组中块的大小都为(1个页面),第1组中块的大小为都为(2个页面),第9组中块的大小都为(512个页面)。也就是说,每一组中块的大小是相同的,且这同样大小的块形成一个链表。伙伴的概念,满足以下三个条件
5、的称为伙伴: (1)两个块大小相同 (2)两个块地址连续 (3)两个块必须是从同一个大块中分离出来的。内存的释放,是分配的逆过程,也可以看作是伙伴的合并过程。当释放一个块时,先在其对应的链表中考查是否有伙伴存在,如果没有伙伴块,就直接把要释放的块挂入链表头;如果有,则从链表中摘下伙伴,合并成一个大块,然后继续考查合并后的块在更大一级链表中是否有伙伴存在,直到不能合并或者已经合并到了最大的块(2 个页面)。位图法,通常我们用位图来实现整个过程中,位图的某一位对应两个互为伙伴的块,为 l 表示其中一块分配出去了,为0表示两块都空闲。伙伴算法中无论是分配还是释放内存块都只对相应的位图位进行异或操作。
6、分配内存时对位图的操作是为释放过程服务,释放过程根据位图判断伙 伴是否存在,如果对相应位的异或操作得 1,则没有伙伴可以合并,如果异或操作得0,就进行合并,并且继续按这种方式合并伙伴,直到不能合并为止。如上所述,伙伴算法综合利用了位图和链表的方式,较为高效地实现了内存的分配和释放,而且在释放过程中尽可能的合并小块内存,有效的消除了内存碎片。伪代码struct node/*建立结构数组,定义链表链接*/ int much; int flag,flag_left,flag_right; struct node *leftchild,*rightchild,*father;/左右儿子父亲指针;str
7、uct IN_T int num; int space; struct node *temp;void search_tree(struct node *head,int space,int really_need)/处理内存请求 内存节点的最大可用空间比应该分配内存小时 分配失败 否则 如果内存节点的大小正好等于应该分配内存 分配成功 否则 如果该内存节点尚未分配子女节点 分配左右子女 递归处理该请求,依照做子女先分配 否则 若左右子女最小的可用空间比需要的内存还大 取小者分配内存 否则 取可用空间比需要的内存大者分配内存 修改节点可用空间与碎片大小 void back_to_space(i
8、nt i) 从释放节点依次向上经过每一个父节点,都要做释放内存的操作。 运行结果演示随机生成的20组内存分配请求 这是内存分配结果,其中,对于内存不够时会有显示,提示最佳置换算法基本思想:发生缺页时,有些页面在内存中,其中有一页见很快被访问(也包含紧接着的下一条指令的那页),而其他页面则可能要到10、100或者1000条指令后才会被访问,每个页面都可以用在该页面首次被访问前所要执行的指令数进行标记。伪代码实现void OPT() for(i=0;iLEN;i+) 如果帧已经填满 若在帧中找到该页命中,退出 否则找到最远使用的页面置换 若帧未填满 命中,则退出否则,加入空闲帧中运行结果演示先进先
9、出法(Fisrt In First Out)基本思想:FIFO最简单的页置换算法,FIFO的页置换的算法为每个页记录着该页调入内存的时间。当必须置换一页时,将选择最旧的页。注意并不需要记录调入一页的确切时间,可以创建一个FIFO队列来管理内存中的所有页。队列中的首页将被置换。当需要调入页时,将它加入到队列的尾部。 FIFO的页置换算法很好理解和实现,但是,其性能并不是很好。所替代的页可能是很久以前使用的、现已不再使用的初始化模块,另一方面,所替代的页可能包含一个以前初始化的并且不断使用的常用变量。伪代码实现void FIFO() for(i=0;iLEN;i+) 如果帧已经填满若在帧中找到该页
10、命中,退出 否则找到最先进入的页面置换 若帧未填满 命中,则退出否则,加入空闲帧中 运行结果演示 最近最久未使用(Least Recently Used)基本思想:LRU置换为每个页关联该页上次使用的时间。当必须置换一次的时候,LRU选择最长时间没有使用的页,这种策略为向后看最优页置换算法。LRU置换算法被认为相当不错,其主要问题是如何实现LRU置换,页帧的排序序列按页帧上次使用时间来定,有两种可行方法: 计算器 为每个页表项关联一个使用时间域,并为CPU增加一个逻辑时钟或者计数器。对每次内存引用,计算器都会增加,每次内存引用的时候时钟寄存器的内容会被复制到相应页所对应的页表项的使用时间域内。
11、用这种方式就得到每页的最近使用时间。置换具有最小时间的页。这种方案需要搜索页表已经查找LRU也,且每次内存访问都要写入内存。在改变页表时,因CPU调度,也必须保持时间。必须考虑时钟溢出。栈 每当引用一个页,该页就从栈中删除并放在顶部。这样,栈顶部总是最近使用的页,栈底部总是LRU页。由于必须是从栈中删除项,所以,该栈可实现为具有头部指针和尾指针的双向链表。虽然每个更新有点费事,但是置换不需要搜索;尾部指针指向栈底部,就是LRU页。伪代码实现void LRU() for(i=0;iLEN;i+) 如果帧已经填满 若在帧中找到该页命中,并将该页放到帧的最后,标志最近使用退出 否则找到最近最不常用的
12、页面置换 若帧未填满命中,则退出否则,加入空闲帧中运行结果演示最不经常使用法(Least Frequently Used)基本思想:即LFU算法(Least Frequently Used algorithm)。这种算法选择近期最少访问的页面作为被替换的页面。显然,这是一种非常合理的算法,因为到目前为止最少使用的页面,很可能也是将来最少访问的页面。该算法既充分利用了主存中页面调度情况的历史信息,又正确反映了程序的局部性。该算法在需要淘汰某一页时,首先淘汰到当前时间为止,被访问次数最少的那一页。该算法只要在页表中给每一页增设一个访问计数器即可实现。每当该页被访问时,访问计数器加 1 ,而发生一次
13、缺页中断时,则淘汰计数值最小的那一页,并将所有的计数器清零。伪代码实现void LFU()/最不经常使用法 for(i=0;iLEN;i+) 如果帧已经填满 若在帧中找到该页命中,该页面标志计数器加1,退出 否则找到计数值最小的页面置换 若帧未填满,命中该页面标志计数器加1,退出否则,加入空闲帧中运行结果演示 最近未使用法(No Used Recently)基本思想:该算法在需要淘汰某一页时,从那些最近一个时期内未被访问的页中任选一页淘汰。NRU为操作系统请求分页存储管理中的页面淘汰算法,又名近似的LRU置换算法。当一存储块中的页面访问时,其相应的“页面访问”位由硬件自动置“”,而由页面管理体
14、制软件周期性地(设周期为,其值通常为几百毫秒),把所有的页面访问位重新置为“”。这样,在时间内,某些被访问的页面,其对应的访问位为“”而未访问的页面,其对应的访问位为“”。查寻页面访问位为“”的页面。在查找过程中,那些被访问的页所对应的访问位被重新置为 “”。由此可见,实际上这种近似LRU算法,已经退化成一种“最近不用”的算法NRU(Not Recently Used)。伪代码实现void NUR()/最近未使用法void NRU()/最近未使用法 for(i=0;iLEN;i+) 模拟周期性将每一页的计数器清0如果帧已经填满 若在帧中找到该页命中,该页面标志计数器置1退出 否则找到计数值为0
15、的页面置换,并将新页面计数器置1 若所有计数值为1,则选首页置换 若帧未填满,命中,该页面标志计数器置1,退出否则,加入空闲帧中,并将新页面计数器置1 运行结果演示五、各种算法运行综合比较因为每个算法在运行时请求是随机分配的,所以要比较不同算法的优劣,需要将不同的算法放在一个程序中,并行执行,打印在一块,方便观察综上比较,帧较少时,OPT算法命中率较高。其次是LRU。六、实验心得1:要编程的第一件事情是先把这个算法的思想弄明白,开始编伙伴算法时,没有考虑合并状况,没有设置对于相同伙伴的合并的判断位,以至于到了后来只能重新修改结构2:没搞清楚伙伴的定义,未考虑到伙伴的地址必须块地址连续,强行将两
16、个大小相同,但地址不知道连不连续的块合并在一起,最后发现这样会导致程序停止运行,这个问题一直到写报告时,将书上相关知识写入文档时才发现,然后豁然开朗,改变了合并方式,程序就可以正常运行了。这个经历也让我发现了边写实验报告,边编程的重要性,比一味的编程更重要的是,在发现错误时,首先要考虑的是自己的编程思想是否正确,其次才是语法问题。3:合并是一个链式问题,不是合并一次就可以的,而是每一次合并都要一直检测,在更大一级的链表中是否有伙伴存在,直到不能合并或者已经合并到了最大块为止。4:位图法是一种很好的标志位方法。有了它可以很方便的寻找空闲块,并进行相关的合并分配操作。用二进制的思想考虑问题,有时候
17、可以事半功倍。5:很久以前是老师强制我们在编程前先画流程图,做为作业的一部分,现在,我发现先画个流程图或者先写个伪代码能让人先对程序的整体框架有一个把握,就像树的主干一样。如果流程图画对了的话,接下来把程序的具体实现填充进去,就可以很方便的实现程序功能了。在伙伴算法实现中,因为没有从全局出发,导致的反复的修改程序,让我体会到了一定要画出正确的流程图,或是先写对伪代码再进行编程的重要性。在实验报告中,为了简洁,我没有附加流程图,但是将相应的伪代码写入报告,来表明编程思想。6:下面对于五种页面的置换算法进行了编程实验。这部分实现起来就比较简单了,因为老师给出了实现的分析,对于如何入手写出了详细的方
18、法。“对于虚拟存储区和内存工作区的不同算法,其中主要的流程:首先用srand( )和rand( )函数定义和产生指令序列,然后将指令序列变换成相应的页地址流,并针对不同的算法计算出相应的命中率。”并给出了具体的例子来帮我们分析实验。这部分的难点就在于区分不同的算法,并对各种算法的排序问题和取代给出自己的方法。7:最佳置换算法使用的是最远使用的页面置换,又叫向前看最优置换算法。而LRU选择最长时间没有使用的页,这种策略为向后看最优页置换算法。8:LRU置换算法关于页帧的排序序列按页帧上次使用时间来定,有两种可行方法:计算器和栈。在这次实验中我选用了计算法方法,这只需要加标志位即可,用栈的话还得另
19、外再加操作,比较复杂。9:在打印各种算法的命中率时,我想按照制表法来打印,结果发现总是有错位现象,因为每次请求是随机分配,所以无法限定打印方式,总是有错位现象。10:这次实现,通过编程的实验,让我对各种算法的原理了解的更为深刻,同时也熟悉了一下编程的方法,将C编程方法温习了一下。温故而知新,学到了很多。七、程序源代码伙伴算法#include#include#include#includestruct node int much; int flag,flag_left,flag_right; struct node *leftchild,*rightchild,*father;struct IN
20、_T int num; int space; struct node *temp;struct IN_T str20;struct node *root,*temp_root,*temp;struct node *temp_l,*temp_r;int total_space=1024,space_num1025;int apply_num=0,release_num=0,find_space=0,str_lock20;void produce_num() int i; for(i=0;i0) space_numt-; total_space=total_space-t; return t; e
21、lse int temp=t; t=t*2; while(t0) space_numt-; int temp_2=t/2; while(temp_2=temp) space_numtemp_2+; temp_2=temp_2/2; total_space=total_space-temp; break; else t=t*2; if(tmuch=space&(head-flag=0) if(space=really_need) head-flag=1; temp=head; else int x=space/really_need; x=x/2; while(x) temp_l=(struct
22、 node*)malloc(sizeof(struct node); temp_r=(struct node*)malloc(sizeof(struct node); head-flag=1; head-leftchild=temp_l; head-rightchild=temp_r; temp_l-father=head; temp_l-much=(head-much)/2; temp_l-flag=1; temp_l-leftchild=NULL; temp_l-rightchild=NULL; temp_r-father=head; temp_r-much=(head-much)/2;
23、temp_r-flag=0; temp_r-leftchild=NULL; temp_r-rightchild=NULL; x=x/2; head=head-leftchild; temp=head; search_tree(head-leftchild,space,really_need); search_tree(head-rightchild,space,really_need); void back_to_space(int i) struct node *tempx=(struct node*)malloc(sizeof(struct node); int or_not=0; tot
24、al_space=total_space+stri.space; tempx=stri.temp; printf(already release the %d of %dn,stri.space,stri.num); tempx-flag=0; space_numtempx-much+; tempx=tempx-father; if(tempx) if(tempx-leftchild-flag=0) tempx-flag_left=0; else tempx-flag_left=1; if(tempx-rightchild-flag=0) tempx-flag_right=0; else te
25、mpx-flag_right=1; while(tempx!=NULL)&(tempx-flag+(tempx-flag_left)+(tempx-flag_right)=1) tempx-flag=0; tempx-flag_left=tempx-flag_right=0; space_num(tempx-leftchild)-much=space_num(tempx-leftchild)-much-2; space_numtempx-much+; tempx-leftchild=tempx-rightchild=NULL; tempx=tempx-father; if(tempx) if(
26、tempx-leftchild-flag=0) tempx-flag_left=0; else tempx-flag_left=1; if(tempx-rightchild-flag=0) tempx-flag_right=0; else tempx-flag_right=1; int how_much_space(int a) if(a512) return 1024; if(a256) return 512; if(a128) return 256; if(a64) return 128; if(a32) return 64; if(a16) return 32; if(a8) retur
27、n 16; if(a4) return 8; if(a2) return 4; if(a1) return 2; else return 1;DWORD WINAPI release() while(1) Sleep(rand()%3); if(apply_num) int c=rand()%apply_num; if(str_lockc=0) back_to_space(c); str_lockc=1; release_num+; if(release_num=20) break; DWORD WINAPI apply() while(1) Sleep(rand()%3); int t=ho
28、w_much_space(strapply_num.num); /need how big space if(total_space=t) int have_space=search(t); if(have_space) temp_root=root; search_tree(temp_root,have_space,t); strapply_num.space=t; strapply_num.temp=(struct node*)malloc(sizeof(struct node); strapply_num.temp=temp; printf(already give %d the %dn,strapply_num.num,t); apply_num+; if(appl
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1