从总体上把握Linux内存管理

注:作者水平有限,欢迎对本文提出宝贵意见。

1.建立内存碎片的概念

          内存碎片分为:内部碎片和外部碎片

 1.1 内部碎片

        内部碎片就是已经被分配出去(能明确指出属于哪个进程)却不能被利用的内存空间;
      内部碎片是处于区域内部或页面内部的存储块。占有这些区域或页面的进程并不使用这个块。而在进程占有这块存储存储块时,系统无法利用它。直到进程释放它,或进程结束时,系统才有可能利用这个存储块。
      造成原因:系统为了一小段连续内存区的需要,不得已给它分配了一大块连续内存,从而造成了内存浪费。

 1.2 外部碎片

         外部碎片指的是还没有被分配出去(不属于任何进程),但由于太小了无法分配给申请内存空间的新进程的内存空闲区域。
     外部碎片是出于任何已分配区域或页面外部的空闲存储块。这些存储块的总和可以满足当前申请的长度要求,但是由于它们的地址不连续或其他原因,使得系统无法满足当前申请。

     造成原因:系统虽有足够的内存,但却都是分散的碎片,无法满足对大块“连续内存”的需求。

2.伙伴算法

2.1 伙伴机制原理

       Buddy System是一种经典的内存管理算法。在Unix和Linux操作系统中都有用到。其作用是减少存储空间中的空洞、减少外部碎片、增加利用率。

     伙伴算法把内存块按大小分组管理,即将所有空闲页帧分组为11个块链表(block List),每个块链表分别包含1,2,4,8,16,32,64,128,256,512,1024个连续的页帧(也就是2的0次方,到2的10次方)。

    我们通过一个简单的例子来说明该算法的工作原理。

    假设要求分配的块其大小为128个页面(由多个页面组成的块我们就叫做页面块)。

    (1)该算法先在块大小为128个页面的链表中查找,看是否有这样一个空闲块。如果有,就直接分配;

    (2)如果没有,该算法会查找下一个更大的块,具体地说,就是在块大小为256个页面的链表中查找一个空闲块。如果存在这样的空闲块,内核就把这256个页面分为两等份,一份分配出去,另一份插入到块大小为128个页面的链表中。

     (3)如果在块大小为256个页面的链表中也没有找到空闲页块,就继续找更大的块,即512个页面的块。如果存在这样的块,内核就从512个页面的块中分出128个页面满足请求,然后从384个页面中取出256个页面插入到块大小为256个页面的链表中。然后把剩余的128个页面插入到块大小为128个页面的链表中。

     (4)如果512个页面的链表中还没有空闲块,就继续找更大的块,即1024个页面的块。重复类似上面的过程。如果在1024页面的块链表中还没找到,则返回出错信号。

       以上过程的逆过程就是块的释放过程,这也是该算法名字的来由。页帧块在释放时,会主动将两个连续的页帧块合并为一个较大的页帧块。

      满足以下条件的两个块称为伙伴:

·      两个块的大小相同

·      两个块的物理地址连续

    伙伴算法把满足以上条件的两个块合并为一个块,该算法是迭代算法,如果合并后的块还可以跟相邻的块进行合并,那么该算法就继续合并。

     由于页帧的分配不再盲目,因此在一定程度上减轻了外部碎片,但并未彻底解决外部碎片的问题。

     与此同时,伙伴系统还带来了很多内部碎片。  

      Linux内核通过slab机制来解决内部碎片的问题。

3.Slab/Slub机制

3.1 出现背景

      Linux内核以页为最小单位分配内存.但内核最常使用的内存往往很小,远小于1页4K,如文件描述符fd,进程描述符PID,虚拟内存区描述符等。根据前面的理论,“内部碎片就是已经被分配出去(能明确指出属于哪个进程)却不能被利用的内存空间”,可见这种情况下容易导致内部碎片问题。此外,内核通常依赖于这些小对象的分配,因此这些对象会在系统生命周期内进行无数次分配(包括初始化、构造及销毁),影响系统的性能。

3.2 工作原理

     为了满足对这种小内存块的需要,Linux内核采用了一种“slab缓存分配器”的技术,其核心思想就是“存储池”的运用。

      小块内存被看做对象,当被使用完后,并不直接释放,而是被缓存到存储池里,下次可继续使用,这无疑避免了频繁创建和销毁对象带来的系统开销。

      slab分配器还支持通用对象的初始化,从而避免了为同一目的而对同一对象重复进行初始化。

猜你喜欢

转载自blog.csdn.net/acs713/article/details/8767660