伙伴算法原理简介

引出

正好最近在做内存相关的一些开发也研究,看到了一个叫伙伴算法的东西,学习一下并做个分享。

  • 内存碎片:内存碎片就是内存被分割成很小很小的一些块,这些块虽然是空闲的,但是却小到无法使用。随着申请和释放次数的增加,内存将变得越来越不连续。最后,整个内存将只剩下碎片,即使有足够的空闲页框可以满足请求,但要分配一个大块的连续页框就可能无法满足,危害:浪费内存空间。我们需要尽量避免产生内存碎片。

  • 举个例子:
    比如我有一块256K的连续内存空间。并且要申请的是连续内存

    1. 申请32K,申请64K,申请64K,还剩96 K
    2. 这时我释放了第一个32K,然后要申请128K
    3. 其实内存够大,但是这128K不连续,我就无法申请出来, 就只能使用额外内存。这样就造成了空间的浪费,也就是内存碎片的产生。

内核在频繁的请求和释放不同大小的一组连续页框,必然会导致在已经分配的块内分散了许多小块的空闲页框。由此带来的问题是,及时有足够的空闲页框可以满足请求,但是要分配一个大块的连续页框就无法满足。

针对这样的问题,有很多行之有效的解决方法,其中伙伴算法被证明是非常行之有效的一套内存管理方法,因此也被相当多的操作系统所采用。是为了核心内存管理能够快速响应请求,尽可能地在提高内存利用率的同时减少内存碎片的一种算法。

实现

伙伴定义

首先看伙伴关系的定义为:由一个母实体分成的两个各方面属性一致的两个子实体,这两个子实体就处于伙伴关系。
在操作系统分配内存的过程中,一个内存块经常被分成两个大小相等的内存块,这两个大小相等的内存块就处于伙伴关系。
它满足3个条件:

  1. 两个块具有相同大小;
  2. 物理地址是连续的;
  3. 从同一个大块中拆分出来。

思想

Linux在分配内存时把所有的空闲页框(4K)分组为11个块链表。
每一块链表分别包含大小为1,2,4,8,16,32,64,128,256,512 和 1024 个连续的页框。对1024 个页框的最大请求对应着 4MB 大小的连续RAM 块。每一块的第一个页框的物理地址是该块大小的整数倍。例如,大小为 16个页框的块,其起始地址是 16 * 2^12 (2^12 = 4096,这是一个常规页的大小)的倍数。
伙伴算法每次只能分配2的幂次页的空间,比如一次分配1页,2页,4页,8页,…,1024页(2^10)等等,每页大小一般为4K,因此,伙伴算法最多一次能够分配4M的内存空间。

在这里插入图片描述

申请空间举例

  1. 假设要申请1M连续空间 4K * 256, 256 = 2^8,我们就可以定位到下标为8的位置,看是否是空闲的。空闲则直接给他分配。
  2. 如果不空闲,就去512个页框也就是2M的链表中找,这个2M链表是否空闲。如果不空闲则继续向下找。
  3. 找到最后一个4M链表,如果没有空闲则分配失败返回错误。如果有空闲,则将其中1M分配给程序。剩余2M和1M分别放到对应的链表中。

总结:就是在分配内存时,首先从空闲的内存中搜索比申请的内存大的最小的内存块。如果这样的内存块存在,则将这块内存标记为“已用”,同时将该内存分配给应用程序。如果这样的内存不存在,则操作系统将寻找更大块的空闲内存,然后将这块内存平分成几部分,一部分返回给程序使用,另一部分作为空闲的内存块放到相应链表中。

释放空间举例

前面释放过程并没有体现出伙伴这个词的含义。伙伴这个词是在释放空间的时候展现出来的。

假设要释放一个1M =256 * 4K的块,看这个过程

  1. 释放是申请的逆过程,当释放一个内存块后。
  2. 先检查所在链表中该位置是否有他的伙伴(定义见前面)。
  3. 如果不存在,则直接释放直接将释放的块插入链表头。如果他所在链表中有伙伴存在,则将其从链表摘下,合并成一个大块,然后继续向后查找合并后的块在更大一级链表中是否有伙伴的存在,直至不能合并或者已经合并至最大块2^10为止。

伙伴合并

  1. 两个块具有相同大小;
  2. 物理地址是连续的;
  3. 从同一个大块中拆分出来。
    在这里插入图片描述

通过一个更具体例子来看优缺点

有一块1M的内存,我们使用伙伴算法来管理
在这里插入图片描述

  1. 图b - 如果程序1想要申请一块45K大小的内存,则系统会将第一块64K的内存块分配给该程序(产生内部碎片为代价)
  2. 图c - 程序2申请一块68K大小的内存,系统会将128K内存分配给该程序;
  3. 图d - 程序3要申请一块大小为35K的内存。系统将空闲的64K内存分配给该程序
  4. 图e - 程序4需要一块大小为90K的内存。当程序提出申请时,系统本该分配给程序D一块128K大小的内存,但此时内存中已经没有空闲的128K内存块了,于是根据伙伴算法的原理,系统会将256K大小的内存块平分,将其中一块分配给程序D,另一块作为空闲内存块保留,等待以后使用
  5. 图f - 程序3释放了它申请的64K内存。在内存释放的同时,系统还负责检查与之相邻并且同样大小的内存是否也空闲,由于此时程序1并没有释放它的内存,所以系统只会将程序3的64K内存回收
  6. 图g - 然后程序1也释放掉由它申请的64K内存,系统随机发现与之相邻且大小相同的一段内存块恰好也处于空闲状态,也就是伙伴内存。于是,将两者合并成128K内存;
  7. 图h - 之后程序2释放掉它的128k,系统也将这块内存与相邻的128K内存合并成256K的空闲内存;
  8. 图i - 最后程序4也释放掉它的内存,经过三次合并后,系统得到了一块1024K的完整内存。

通过这个例子我们能发现:通过伙伴算法可以更好的动态管理内存,如果没有伙伴合并,那么中间的很多内存会被打散并且留下很多碎片。

优缺点

伙伴算法,简而言之,就是将内存分成若干块,然后尽可能以最适合的方式满足程序内存需求的一种内存管理算法,伙伴算法的一大优势是它能够避免外部碎片的产生。所以伙伴算法虽然能够完全避免外部碎片的产生,但这恰恰是以产生内部碎片为代价的
如何消灭内部碎片,好像有一个叫slab算法。

内部碎片和外部碎片

  • 内部碎片 是处于 (操作系统分配的用于装载某一进程的内存)区域内部 或页面内部 的存储块。占有这些区域或页面的进程并不使用这个 存储块。而在进程占有这块存储块时,系统无法利用它。直到进程释放它,或进程结束时,系统才有可能利用这个存储块。
    单道连续分配只有内部碎片。 多道固定连续分配既有内部碎片,又有外部碎片。
  • 外部碎片指的是不同进程间的,还没有被分配出去(不属于任何进程),但由于太小了无法分配给申请内存空间的新进程的内存空闲区域。

数据结构

在这里插入图片描述

在这里插入图片描述

参考

https://blog.csdn.net/wenqian1991/article/details/27968779
https://cloud.tencent.com/developer/article/1661507
https://www.cnblogs.com/cherishui/p/4246133.html

  • 15
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Charles Ray

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值