操作系统--------内存管理(2)

来源:互联网 发布:java程序员的工作内容 编辑:程序博客网 时间:2024/06/05 08:15

由于固定分配和动态分配都存在着问题,固定分配可能会造成空间的浪费,而动态分配在内存回收的时候也存在着问题。所以改进之后出现了伙伴系统。


伙伴系统:

将内存的用户空间部分全部划分为2的i次幂容量大小的块,无论是已分配区域还是空闲区域 ,每一块都是2的i次幂大小。当一个程序想进入内存的时候,要找最接近它所需要的容量的内存空间,将它放进去。虽然,外碎片没有,但是内碎片还是一直存在。

伙系统中的重要的数据结构:空闲双向链表

随着内存不断的分配回收,将所有的空闲分区做成很多个双向链表,每个双向链表 上面的节点都是空闲分区大小相同的区域,这样的话下次来找合适的空间的时候可以直接遍历这个链表,而且,之所以 设计成链表这种结构,是因为空闲分区和已分配分区是变化很快的,普通的线性表所需要的时间开销太大,所以采用链表。最极端的情况,假设内存可用的空间一共是2的n次方,那么如果全是空闲区,就有n+1个链表,大小从1----2的n次方不等。



void get_hole(int i){   if(i==(U+1))<failure>;  超过最大容量    if(<i_list empty>) 无2I空闲块       { get_hole(i+1);  取2i+1的块         <split hole into buddies>; 分成2个伙伴         <put buddies on i_list>;  2i块挂空闲队列  }    <take first hole on i_list> 有则直接取第一个}

伙伴系统中寻找合适的内存空间的算法如上所示,采用递归的算法来实现,第一次调用的时候,函数会递归到 最边界,然后依次把整个内存一份为二,之后会依次回退到第一次进入递归的时候,每次把低地址 部分的空间优先分配,不断的将低地址的空间一分为二,最后把2的i+1次方的空间再次一份为二剩下的两份空间里面的任意一份都符合程序的要求,分配给他即可。


伙伴系统在回收内存空间的时候,如果有两个空闲区,如果两者是伙伴,那么可以合并为一个,如果不是伙伴则不能合并。只要是伙伴有空闲的,自己也是空闲那么就合并,也就是说,如果内存中的程序依次运行完了之后,回收之后,整个内存完好如初,就像没有分配的时候一样。这样一来,伙伴系统即继承了动态分配的灵活性,也继承了固定分配外碎片少的优势,更加改善了动态内存中内存回收困难的问题。


那么,如何在回收的时候计算你的伙伴地址呢?

首先,伙伴系统的内存分配情况可以用二叉树 清晰的表述出来

先确定你所在内存区域的首地址,应该以2的次方的形式来表示首地址的大小。依次用你的首地址去 除以你上一级内存空间的地址,如果可以整除,那么所处的为左节点,如果不能整除,那么为右节点,当只要到底是左节点还是右节点的时候,就可以向左或者向右计算出伙伴的首地址,如果正和空闲下来的那个区域的首地址相吻合,那么两个空闲分区便可以合并为一个分区。


但是伙伴系统虽然有所改进,但是当内存被频繁的分割和回收之后,在这其间如果有一个大于任意一个空闲分区大小的程序要调入内存,那么还是不能有效的解决。

动态重定位:



假设动态分配过程中,没有 任何一个空闲分区大于等于这个分区,但是所有 的空闲分区加起来 却符合这个程序的要求,则需要压缩和移动内存中数据的功能,这样一来效率就会变得很低很低。动态重定位方法,每次在程序要调入内存的时候要先检查空闲表是否 有合适的的空间 ,如果没有,那么把一些空闲分区加起来看是否 符合要求,最极端的情况下把所有的空闲分区都加起来。重定位的压缩技术,可以根据硬件中的基址寄存器的内容加上相对地址,该进程的新地址是多少,并且在确定绝对地址之前,要与界限寄存器里面的内容比较,看看更换的新的地址之后是否原来所占的内存空间变大了还是变小了,如果出现了变化那么就说明得到的这个新的逻辑地址是错误的,不能转化为绝对地址。

问题:1. 开销大;2. 移动时机(当找不到足够大的空闲分区且空闲分区的总容量可以满足作业要求时进行拼接)


0 0
原创粉丝点击