转载请注明出处:http://blog.csdn.net/ns_code/article/details/20661785
操作系统中常用来管理内存的动态分配和回收的方法有边界标识法和伙伴系统。
系统将所有的空闲块链接在一个双重循环链表结构的可利用空间表中。系统的特点在于:在每个内存区的头部和底部两个边界上分别设有标识,以标识该区域是占用块或空闲块,使得在回收用户释放的空闲块时易于判别在物理位置上与其相邻的内存区域是否为空闲块,以便将所有地址连续的空闲块组合成一个更大的可利用的空闲块。
可利用空间表的结构如下图所示:
其中space为一组连续的存储单元,是可以分配给用户使用的内存区域,它的大小由头部的size属性指示,头部的llink域和rlink域分别指向上一个可用空间表和下一个可用空间表,底部的uplink域指向本节点头部,头部和底部都有个tag域,用来表示该当前块是空闲块还是占用块。
采用该方法时,内存分配很简单,采用首次拟合分配、最佳拟合分配、最差拟合分配均可。另外,为了避免修改指针,约定将该节点中的高地址部分分配给用户。很明显,如果每次分配都从同一个节点开始查找的话,势必会造成存储量小的节点密集在头指针所指节点附近,这同样会增加查询较大空闲块的时间,因此在每次分配后,令指针指向刚刚分配过的节点的后续节点,这样下一次内存分配时就从不同的节点进行查找,避免了上述问题。
某占用块被释放后,检查其左右邻内存块是否为空闲块,如果是则连接起来成为一个更大的可用内存块,并修改相应的域。
在伙伴系统中,无论是占用块还是空闲块,其大小均为2的整数次幂。比如,如果用户请求申请n个字节的内存区,那么系统分配给它的占用块大小为2^k(2^(k-1) < n = < 2^k)。
可利用的空间表的结构如下图所示:
其中,kva域的值为2的幂次k,space是一个大小为2^k个字的连续内存空间。
为了再分配时方便查找,系统将所有大小相同的空闲块用链表连接在一起,最后再索引到不同kval对应的位置,这有点类似于哈希表的结构。如下图所示:
假设初始可用表中只有2^k大小的空闲块,我们要申请n(2^(k-2) < n = < 2^(k-1))个字节,的内存空间,此时由于节点为2^(k-1)大小的子表为空,则需从节点为2^k的子表中取出一块,将其中的一半分配给用户,剩余的一半作为一个新节点插入到节点大小为2^(k-1)的字表中。同样,若2^(k-i-1) < n =< 2^(k-i),并且所有节点大小小于2^k的子表均为空,则同样需从节点大小为2^k的子表中取出一块,将其中的2^(k-i)的一小部分分配给用户,剩余部分分成若干个节点分别插入到节点大小为2^(k-i)、2^(k-i+1)。。。2^(k-1)的子表中。
下图展示了内存分配的情况:
在伙伴系统中,仅会考虑将互为伙伴的两个空闲块合并在一起。我们在分配时经常需要将一个大的空闲块分裂成两个大小相等的存储区,这两个由同一个大块分裂出来的小块就互为伙伴块。若有两个空闲块,即时大小相同且地址相邻,但如果不是由同一个大块分裂出来的,也不会合并在一起。
起始地址为p,大小为2^k的内存块,其伙伴块的起始地址满足如下关系:
若p%2(k+1)=0,则
伙伴块的起始地址 = p+2^k;
若p%2(k+1)=2^k,则
伙伴块的起始地址 = p-2^k;
回收情况如下图所示:
原文:http://blog.csdn.net/ns_code/article/details/20661785