Linux内存分配与回收——伙伴算法

目录

 

简介

背景

内存管理机制

当前存在的问题

伙伴算法

算法原理

内存分配

内存回收

优缺点分析

参考


简介

        在Linux系统中,内存的分配与回收速率直接影响系统的存取效率。当内核频繁请求和释放不同大小的一组连续页框时,会导致许多外部空闲碎片,造成空间的浪费。使用伙伴算法可以有效地缓解该问题。伙伴关系机制是操作系统中的一种动态存储管理算法。在进行内存分配时,该算法通过不断平分较大的空闲内存块来获得较小的空闲内存块,直到获得所需要的内存块;在进行内存回收时,该算法尽可能地合并空闲块。

背景

内存管理机制

       内存管理是应用程序通过硬件和软件协作访问内存的一种方法,当进程请求内存使用时,它给进程分配可用的内存;当进程释放内存时,回收相应的内存,同时负责跟踪系统中内存的使用状态。

                                                   

       在Linux系统中,首先将内存分为若干个节点,然后每个节点又可以分为1-3个区,每个区下又有若干个页。页是内存管理的基本单元。

当前存在的问题

       当系统工作时,CPU最先访问的地址不是物理内存中的实地址,而是虚拟地址空间的虚地址。当请求分页时,首先在虚拟地址空间中分配一个虚拟空间,然后根据需要为此区间分配相应的物理页面并建立映射。

       在分配空间时,我们首先想到的便是malloc函数。由于在实际情况中,操作系统必须能够在任意时刻申请和释放任意大小的内存,该函数的实现并不容易,导致的主要问题有延时问题和碎片问题。

       延时问题指的是系统查找到可分配单元的时间变长,例如程序请求分配一个64KB的内存空间,系统查看64KB空间发现不全是空余的,于是查看65KB的空间,发现仍不能满足需求,直到查看80KB空间时,才满足了需求,这种方式请求次数多达17次,频繁操作时,非常耗时。

       若系统以较大的定长空间来分配内存,在一定程度上可以节省时间,但带来的是碎片过多问题,由于每次用较大的空间进行分配,系统中出现大量碎片,导致内存浪费。严重者会导致内存无法完成分配,虽然仍有许多碎片空间。

       基于此,系统需要一种能够高效分配内存,同时又能减少产生碎片的算法,伙伴算法能有效地解决该问题,如今已成为操作系统中的一种基础算法。

伙伴算法

算法原理

       伙伴算法是一种动态存储器管理算法。该算法通过不断地平分较大的空闲内存块来获得较小的空闲内存块,直到获得所需要的内存块,当内存释放时,该算法尽可能地合并空闲块。其中,在分配和合并内存块时都是以2的次幂为单位,即1,2,4,8,16,32,64,128等。所谓“伙伴”,就是指在空闲块被分裂时,由同一个大块内存分裂出来的两个小块内存就互称“伙伴”。“伙伴”应当满足以下三个条件:

  • 两个块大小相同
  • 两个块地址连续
  • 两个块必须是同一个大块中分离出来的

       伙伴算法使用位图和空闲链表作为辅助工具,其中位图用于跟踪内存块的使用情况,空闲链表用来维护内存中还没有被分配的块。假设系统的全部可用空间为2^{max} ,则建立一个长度为max+1的链表,链表尾存放大小为2^{max} 的块,如下图所示:

                                                  

        当请求大小为size的空间时, 2^{k-1} < size < 2^k,且k < max。于是系统在链表中寻找大小为 2^k 的块,发现该位置为空,于是继续向下搜寻大小为 2^{k+1} 的块,若还为空,则继续向下搜寻,直到找到不为空的块 2^{max} 。

       该块不为空,于是该块进行分裂,变为两个大小为 2^{max-1} 的块。其中一块插入到链表中 2^{max-1} 的位置,另一块继续分裂。重复此过程,直到分裂产生大小为 2^{k} 大小的块为止,结果如图所示:

                                          

        如图所示,最后一次分裂时,由一个大小为 2^{k+1} 的块分成两个大小均为 2^k 大小的块。将其中一块交给用户使用,另一块加入到空闲链表相应位置中。

       由于进行了多次分裂,链表的同一位置可能会出现多个大小相等的块,此时选用时只需要在表头选取一个即可。当进行合并操作时,只需将大小相等的块合并,然后插入到链表中相应位置即可。  

                                 

      以下用具体实例说明伙伴算法在内存分配与回收中的应用。

内存分配

       下面通过一个例子说明内存分配的过程:

       现内存总容量为16KB,用户请求分配4KB大小的内存空间,且规定最小的内存分配单元是2KB。于是位图分为8个区域,用1表示已分配,用0表示未分配,则初始位图和空闲链表如图所示。从上到下依次是位图、内存块、空闲链表。

                                             

       由于需要分配4KB内存,数显到链表中4KB位置进行查看,发现为空,于是继续向后查找8KB位置,发现仍为空,直到到达链表尾16KB位置不为空。16KB块分裂成两个8KB的块,其中一块插入到链表相应位置,另一块继续分裂成两个4KB的块,其中一个交付使用,另一个插入到链表中,结果如下图所示。

                                            

内存回收

       内存回收是内存分配的逆过程,假设以上存储要释放4KB内存,首先到链表中4KB位置查看是否有它的“伙伴”,发现该位置不为空,于是合并成一个8KB的块,继续寻找它的“伙伴”,然后合并成一个16KB的块,插入链表中。

       若在查找过程中没有发现“伙伴”,则直接插入到链表中,然后将位图中的标记清零,表示内存可用。

优缺点分析

  • 伙伴算法采用2的幂次方进行分配内存块,可以避免把大的内存块拆分的过小,更重要的是可以加快分配和释放速度,但如果所需要的空间不是2的整数次幂,则会产生许多内部碎片。
  • 分配和合并采用链表和位图操作,操作方便,但是开销比较大。
  • 一个很小的块往往会阻碍一个大块的合并,一个系统中,对内存块的分配,大小是随机的,一片内存中仅一个小的内存块没有释放,旁边两个大的内存块就不能合并。

参考

  1. 《Linux操作系统原理与应用》
  2. 《深入理解Linux内核》
  3. https://blog.csdn.net/lcl497049972/article/details/82954124
  4. https://www.cnblogs.com/alantu2018/p/8527821.html

 

 

 

 

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Linux进程调度: Linux使用了完全公平调度算法(CFS)来调度进程。CFS算法是一种基于红黑树的进程调度算法,它通过维护一个红黑树来记录各个进程的运行时间,然后将CPU时间按照进程在红黑树中的位置进行分配,从而保证所有进程获得公平的CPU时间。 CFS算法的优点是能够保证所有进程都能够获得公平的CPU时间,缺点是在高负载情况下,会导致进程间切换的频率较高,造成一定的性能损失。 Linux内存分配回收Linux使用了众多的内存分配算法和回收机制,其中最常见的是页式内存管理和SLAB分配器。 页式内存管理是指将物理内存划分为大小相等的物理页面,然后将虚拟内存划分为大小相等的虚拟页面,从而实现虚拟地址与物理地址的映射。当进程需要访问某个虚拟地址时,Linux会将该地址映射到物理地址,并将物理页面加载到内存中。 SLAB分配器是一种高效的内存分配算法,它将内存分为大小相等的SLAB,然后在每个SLAB中维护一个链表,用于记录该SLAB中可用的内存块。当进程需要分配内存时,Linux会从相应大小的SLAB中获取一个可用的内存块,并将其分配给进程。 Linux内存回收机制主要是通过进程的引用计数来实现的。当进程释放某个内存块时,Linux会检查该内存块是否还被其他进程引用,如果已经没有被引用,则将其回收并释放到SLAB中,以供其他进程使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值