【转载】内存基本概念-伙伴(Buddy)算法

发布时间 2023-12-22 22:27:59作者: 嵌入式与Linux那些事

简介

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

背景

内存管理机制

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

img

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

当前存在的问题

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

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

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

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

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

伙伴算法

算法原理

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

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

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

img

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

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

img

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

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

img

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

内存分配

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

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

img

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

img

内存回收

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

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

优缺点分析

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

原文链接:https://blog.csdn.net/Reticent_Man/article/details/105545226