1、页、页面、页框这三个要先搞清楚
这个图里面的小框框
虚拟地址的叫页,页面
物理地址的叫做框 页框 块。
虚拟地址(页面) -------------------- 页表 ------------------- 物理地址(页框)
页和页面是一个东西(一般是4K)
一个4G的虚拟地址空间,每个页大小4K,1M个页,也就是10^6个页,10^3*10^3,1024*1024
2、伙伴算法解决的是频繁的请求和释放不同大小的一组连续页框,就会导致连续的页框中间分散了许多空闲页框。导致我们无法分配连续的页框。
伙伴算法:把所有空闲的页框分为11个块链表,每块链表中包含特定的连续页框地址空间。
第0个块链表包含大小为2^0个连续的页框 2^0*4K = 4K
第1个块链表包含大小为2^1个连续的页框 2^1*4K = 8K
第2个块链表包含大小为2^2个连续的页框 2^2*4K = 16K
第3个块链表包含大小为2^2个连续的页框 2^3*4K = 32K
。
。
。
第10个块链表包含大小为2^10个连续的页框 2^10*4k = 4M 伙伴算法最多一次能够分配4M的内存空间
3、两个连续的内存块 01 12算是一个伙伴,12不算,(伙伴算法的缺点,slab分配器解决这个问题)
4、伙伴位图
用一位描述伙伴块的状态位码,称为状态位码,bit0代表第0块和第1块的伙伴为嘛,bit0 = 1代表伙伴不是全部空闲,bit0 = 1代表全部空闲。
linux2.6为每个管理区使用不同的伙伴系统,内核空间分为三种区,DMA,NORMAL,HIGHMEM,对于每一种区,都有对应的伙伴算法。
整个内存分布图就是由11个块组链表组成:
切记:每个块组链表的数量是不固定的
举个简单例子:
比如我申请一块16K大小的内存空间,伙伴算法会从第二个快链表中查找有没有空闲的16K,有就分配。
没有:从上一级第三个块链中32k中申请,有就给我16K,剩下的16K划分到第二块链表中去。
没有:还是一样往上走
释放:
比如我释放我的那个16K往上走一看16 16刚好一个32给我的伙伴。这就是申请的逆过程。
伙伴算法的优缺点分析:
优点:
较好的解决了外部碎片问题
当需要分配若干个内存页面时,用于DMA的内存页面必须连续,伙伴算法很好的满足了需求。
只要请求的块不超过512个页面时,内核就尽量分配连续的页面。
针对大内存分配设计
缺点:
合并的要求太过严格,只能是满足伙伴关系的才能合并,1 2就不能合并
碎片问题:一个连续的内存中仅仅一个页面被占用,导致整块内存都不具备合并的条件。
浪费问题:伙伴算法只能分配2的幂次方内存区,当需要9K时就需要分配16K内存,造成浪费
算法效率问题:伙伴算法涉及了比较多的计算还有链表和位图的操作,开销还是比较大的,如果每次2^n大小的伙伴块就会合并到2^(n+1)的链表队列中,那么2^n大小的链表中的块就会因合并减少,随后系统可能会有对该大小块的需求,为此必须再从2^(n+1)中拆分,这样合并有拆分是低效的
总结:
linux针对大内存物理地址分配,采用伙伴算法,如果是针对一个小page的内存,频繁的分配和释放,slab。