通常情况下,一个高级操作系统必须要给进程提供基本的、能够在任意时刻申请和释放任意大小内存的功能,就像 malloc 函数那样,然而,实现 malloc 函数并不简单,由于进程申请内存的大小是任意的,如果操作系统对 malloc 函数的实现方法不对,将直接导致一个不可避免的问题,那就是内存碎片。

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

针对这样的问题,有很多行之有效的解决方法,其中伙伴算法被证明是非常行之有效的一套内存管理方法,因此也被相当多的操作系统所采用。

伙伴算法,简而言之,就是将内存分成若干块,然后尽可能以最适合的方式满足程序内存需求的一种内存管理算法,伙伴算法的一大优势是它能够完全避免外部碎片的产生。什么是外部碎片以及内部碎片,前面博文 slab 分配器后面已有介绍。申请时,伙伴算法会给程序分配一个较大的内存空间,即保证所有大块内存都能得到满足。很明显分配比需求还大的内存空间,会产生内部碎片。所以伙伴算法虽然能够完全避免外部碎片的产生,但这恰恰是以产生内部碎片为代价的。

Linux 便是采用这著名的伙伴系统算法来解决外部碎片的问题。把所有的空闲页框分组为 11 块链表,每一块链表分别包含大小为 1,2,4,8,16,32,64,128,256,512 和 1024 个连续的页框。对1024 个页框的最大请求对应着 4MB 大小的连续 RAM 块。每一块的第一个页框的物理地址是该块大小的整数倍。例如,大小为 16 个页框的块,其起始地址是 16 * 2 ^ 12 (2 ^ 12 = 4096,这是一个常规页的大小)的倍数。

下面通过一个简单的例子来说明该算法的工作原理:

假设要请求一个 256(129 ~ 256)个页框的块。算法先在 256 个页框的链表中检查是否有一个空闲块。如果没有这样的块,算法会查找下一个更大的页块,也就是,在 512 个页框的链表中找一个空闲块。如果存在这样的块,内核就把 512 的页框分成两等分,一般用作满足需求,另一半则插入到 256 个页框的链表中。如果在 512 个页框的块链表中也没找到空闲块,就继续找更大的块 —— 1024 个页框的块。如果这样的块存在,内核就把 1024 个页框块的 256 个页框用作请求,然后剩余的 768 个页框中拿 512 个插入到 512 个页框的链表中,再把最后的 256 个插入到 256 个页框的链表中。如果 1024 个页框的链表还是空的,算法就放弃并发出错误信号。

简而言之,就是在分配内存时,首先从空闲的内存中搜索比申请的内存大的最小的内存块。如果这样的内存块存在,则将这块内存标记为“已用”,同时将该内存分配给应用程序。如果这样的内存不存在,则操作系统将寻找更大块的空闲内存,然后将这块内存平分成两部分,一部分返回给程序使用,另一部分作为空闲的内存块等待下一次被分配。

以上过程的逆过程就是页框块的释放过程,也是该算法名字的由来。内核试图把大小为 b 的一对空闲伙伴块合并为一个大小为 2b 的单独块。满足以下条件的两个块称为伙伴:

  • 两个块具有相同的大小,记作 b 。
  • 它们的物理地址是连续的。
  • 第一块的第一个页框的物理地址是 2 * b * 2^12 的倍数。

该算法是迭代的,如果它成功合并所释放的块,它会试图合并 2b 的块,以再次试图形成更大的块。

假设要释放一个 256 个页框的块,算法就把其插入到 256 个页框的链表中,然后检查与该内存相邻的内存,如果存在同样大小为 256 个页框的并且空闲的内存,就将这两块内存合并成 512 个页框,然后插入到 512 个页框的链表中,如果不存在,就没有后面的合并操作。然后再进一步检查,如果合并后的 512 个页框的内存存在大小为 512 个页框的相邻且空闲的内存,则将两者合并,然后插入到 1024 个页框的链表中。

简而言之,就是当程序释放内存时,操作系统首先将该内存回收,然后检查与该内存相邻的内存是否是同样大小并且同样处于空闲的状态,如果是,则将这两块内存合并,然后程序递归进行同样的检查。

下面通过一个例子,来深入地理解一下伙伴算法的真正内涵(下面这个例子并不严格表示Linux 内核中的实现,是阐述伙伴算法的实现思想):

假设系统中有 1MB 大小的内存需要动态管理,按照伙伴算法的要求:需要将这 1 M 大小的内存进行划分。这里,我们将这 1 M 的内存分为 64K、64K、128K、256K、和 512K 共五个部分,如下图 a 所示

1、此时,如果有一个程序 A 想要申请一块 45 K 大小的内存,则系统会将第一块 64 K 的内存块分配给该程序(产生内部碎片为代价),如图 b 所示;

2、然后程序 B 向系统申请一块 68 K 大小的内存,系统会将 128 K 内存分配给该程序,如图 c 所示;

3、接下来,程序 C 要申请一块大小为 35 K 的内存。系统将空闲的 64 K 内存分配给该程序,如图 d 所示;

4、之后程序 D 需要一块大小为 90 K 的内存。当程序提出申请时,系统本该分配给程序 D 一块 128 K 大小的内存,但此时内存中已经没有空闲的 128 K 内存块了,于是根据伙伴算法的原理,系统会将 256 K 大小的内存块平分,将其中一块分配给程序 D,另一块作为空闲内存块保留,等待以后使用,如图 e 所示;

5、紧接着,程序 C 释放了它申请的 64 K 内存。在内存释放的同时,系统还负责检查与之相邻并且同样大小的内存是否也空闲,由于此时程序A并没有释放它的内存,所以系统只会将程序 C 的 64 K 内存回收,如图 f 所示;

6、然后程序 A 也释放掉由它申请的 64 K 内存,系统随机发现与之相邻且大小相同的一段内存块恰好也处于空闲状态。于是,将两者合并成 128 K 内存,如图 g 所示;

7、之后程序 B 释放掉它的 128 k,系统也将这块内存与相邻的 128 K 内存合并成 256 K 的空闲内存,如图 h 所示;

8、最后程序 D 也释放掉它的内存,经过三次合并后,系统得到了一块 1024 K 的完整内存,如图 i 所示。

有了前面的了解,我们通过Linux 内核源码(mmzone.h)来看看伙伴算法是如何实现的:

伙伴算法管理结构

#define MAX_ORDER 11struct zone {……struct free_area free_area[MAX_ORDER];……}struct free_area {struct list_head free_list[MIGRATE_TYPES];unsigned long nr_free; //该组类别块空闲的个数};

前面说到伙伴算法把所有的空闲页框分组为 11 块链表,内存分配的最大长度便是 2^10 页面。

上面两个结构体向我们揭示了伙伴算法管理结构。zone 结构中的 free_area 数组,大小为 11,分别存放着这 11 个组,free_area 结构体里面又标注了该组别空闲内存块的情况。

将所有空闲页框分为 11 个组,然后同等大小的串成一个链表对应到 free_area 数组中。这样能很好的管理这些不同大小页面的块。

(啊哦,有时间再补充吧...)

(SAW:Game Over!)

OS / Linux / 伙伴(buddy)算法相关推荐

  1. 内存算法-伙伴(buddy)算法

    伙伴(buddy)算法,它不能根据需要从被管理内存的开头部分创建新内存.它有明确的共性,就是各个内存块可分可合,但不是任意的分与合.每个块都有个朋友,或叫"伙伴",既可与之分开,又 ...

  2. 操作系统学习之用C语言模拟伙伴(Buddy)算法

    前言 学到了操作系统的的虚拟内存部分,硬件不太好的我学起来有些吃力,概念性知识点太多,所以我决定用软件的方式,实现一下虚拟内存常用的算法,因为用到了指针,暂时用C语言写一下Buddy算法.FIFO算法 ...

  3. fat32文件系统的实现与buddy算法

    报告一    FAT32文件系统的实现 文件系统(File System)是计算机系统必不可少的组成部分,可以说除了部分结构简单的单片机系统之外,文件系统是支撑每一个计算机系统运行的最重要的支撑,无论 ...

  4. linux为系统分配内存,Linux操作系统知识讲解:走进Linux 内存分配算法

    Linux 内存分配算法 内存管理算法--对讨厌自己管理内存的人来说是天赐的礼物 1.内存碎片 1) 基本原理 产生原因:内存分配较小,并且分配的这些小的内存生存周期又较长,反复申请后将产生内存碎片的 ...

  5. hadoop mac和linux,How to Install Hadoop? (On Mac OS,Linux or Cygwin on Windows)

    摘要: How to Install Hadoop?(On Mac OS, Linux or Cygwin on Windows)1)Download hadoop 0.20.0 from http: ...

  6. 第一节:linux 开发AI算法以及libtorch部署算法详细教程-环境搭建

    文章目录 linux 开发AI算法以及部署算法详细教程 linux docker环境搭建 参考example-docker libtorch 安装教程 opencv 安装教程 编译过程 测试开发环境示 ...

  7. OS + linux command / Linux Command / Linux command / linux Command

    写下你职业生涯中最难以忘怀的误操作.. http://www.dangkai.com/ArticlePage/Article59549.htm http://bbs.chinaunix.net/thr ...

  8. bt5 mysql root_MySQL_Linux利用UDF库实现Mysql提权,环境: os:linux(bt5)database - phpStudy...

    Linux利用UDF库实现Mysql提权 环境: os:linux(bt5) database:mysql 简述: 通过自定义库函数来实现执行任意的程序,这里只在linux下测试通过,具体到windo ...

  9. 时间片轮转调度 java_java OS时间片轮转调度基本算法

    java OS时间片轮转调度基本算法实例源码讲解. public class OSprocess{ private String name; //进程名 private OSprocess next; ...

最新文章

  1. python3中zip()函数的用法
  2. 基于jquery的侧边栏分享导航
  3. robot连接mysql_robot连接mysql - autocar - 51Testing软件测试网 51Testing软件测试网-软件测试人的精神家园...
  4. Docker小白到实战之Dockerfile解析及实战演示,果然顺手
  5. web编程 模块1 html,PYcore python programming笔记C20 Web编程
  6. LIDAR in Google Earth
  7. sweetalert插件的使用
  8. git新建账号_github 账号创建
  9. 索尼android 8,索尼宣布Android 8.0升级名单 Z系列被抛弃
  10. linux 负载高ssh连不上,关于ssh连不上问题的解决方法(必看)
  11. 家长如何使自己的孩子轻松快乐地学好数学和英语?
  12. 51单片机入门(4)蜂鸣器(无源+有源)
  13. HDU 5755 Gambler Bo(高斯消元裸题)——2016 Multi-University Training Contest 3
  14. SeedLab1: Sniffing Spoofing Lab
  15. unrecognized or represents more than one time zone
  16. 计算机输入法切换用户,电脑的输入法切换不了怎么办,教您如何解决电脑系统输入法...
  17. 究竟什么时候该看哪个后视镜?老司机用经验告诉你答案
  18. item_search_shop -VVIC 商家商品列表
  19. 手机截图并传输到电脑工具
  20. 算法笔记——基数排序

热门文章

  1. error LNK2001: 无法解析的外部符号 __imp__Shell_NotifyIconA@8
  2. 通过案例学调优之--SQL Profile
  3. PHP-Codeigniter:实习笔记1
  4. RIM发警告 部分黑莓手机存在安全漏洞
  5. 17、有名管道与无名管道之间的区别
  6. js es6 reduce用法示例:实现数据累加
  7. IO多路复用概念介绍
  8. 7-Zip将一个大文件压缩成多个小的压缩包
  9. golang管道channel的基本使用:读、写数据到管道
  10. scala方法定义示例