本文将简要介绍一下Linux内核中的伙伴分配算法。

Technorati 标签: 伙伴算法

    算法作用

     它要解决的问题是频繁地请求和释放不同大小的一组连续页框,必然导致在已分配页框的块内分散了许多小块的空闲页面,由此带来的问题是,即使有足够的空闲页框可以满足请求,但要分配一个大块的连续页框可能无法满足请求。

     伙伴算法(Buddy system)把所有的空闲页框分为11个块链表,每块链表中分布包含特定的连续页框地址空间,比如第0个块链表包含大小为2^0个连续的页框,第1个块链表中,每个链表元素包含2个页框大小的连续地址空间,….,第10个块链表中,每个链表元素代表4M的连续地址空间。每个链表中元素的个数在系统初始化时决定,在执行过程中,动态变化。

     伙伴算法每次只能分配2的幂次页的空间,比如一次分配1页,2页,4页,8页,…,1024页(2^10)等等,每页大小一般为4K,因此,伙伴算法最多一次能够分配4M的内存空间。

    核心概念和数据结构

      两个内存块,大小相同,地址连续,同属于一个大块区域。(第0块和第1块是伙伴,第2块和第3块是伙伴,但第1块和第2块不是伙伴)

      伙伴位图:用一位描述伙伴块的状态位码,称之为伙伴位码。比如,bit0为第0块和第1块的伙伴位码,如果bit0为1,表示这两块至少有一块已经分配出去,如果bit0为0,说明两块都空闲,还没分配。

      Linux2.6为每个管理区使用不同的伙伴系统,内核空间分为三种区,DMA,NORMAL,HIGHMEM,对于每一种区,都有对于的伙伴算法,

      1. free_area数组:

       

   

   1:  struct zone{
   2:     ....
   3:     struct free_area    free_area[MAX_ORDER];  
   4:     ....
   5:  }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

   struct free_area  free_area[MAX_ORDER]    #MAX_ORDER 默认值为11

      2.  zone_mem_map数组

     

   free_area数组中,第K个元素,它标识所有大小为2^k的空闲块,所有空闲快由free_list指向的双向循环链表组织起来。其中的nr_free,它指定了对应空间剩余块的个数。

   整个分配图示,大概如下:

 

 

    

    申请和回收过程

      比如,我要分配4(2^2)页(16k)的内存空间,算法会先从free_area[2]中查看nr_free是否为空,如果有空闲块,则从中分配,如果没有空闲块,就从它的上一级free_area[3](每块32K)中分配出16K,并将多余的内存(16K)加入到free_area[2]中去。如果free_area[3]也没有空闲,则从更上一级申请空间,依次递推,直到free_area[max_order],如果顶级都没有空间,那么就报告分配失败。

      释放是申请的逆过程,当释放一个内存块时,先在其对于的free_area链表中查找是否有伙伴存在,如果没有伙伴块,直接将释放的块插入链表头。如果有或板块的存在,则将其从链表摘下,合并成一个大块,然后继续查找合并后的块在更大一级链表中是否有伙伴的存在,直至不能合并或者已经合并至最大块2^10为止。

     内核试图将大小为b的一对空闲块(一个是现有空闲链表上的,一个是待回收的),合并为一个大小为2B的单独块,如果它成功合并所释放的块,它会试图合并2b大小的块,

     内核使用_rmqueue()函数来在管理区中找到一个空闲块,成功返回第一个被分配页框的页描述符,失败返回NULL。

       内核使用

    __free_pages_bulk()函数按照伙伴系统的策略释放页框。它使用3个基本输入参数:

    page:被释放块中所包含的第一个页框描述符的地址。

    zone:管理区描述符的地址。

    order:块大小的对数。

伙伴算法的优缺点

    优点:

     较好的解决外部碎片问题

     当需要分配若干个内存页面时,用于DMA的内存页面必须连续,伙伴算法很好的满足了这个要求

     只要请求的块不超过512个页面(2K),内核就尽量分配连续的页面。

     针对大内存分配设计。

 

     缺点:

      1. 合并的要求太过严格,只能是满足伙伴关系的块才能合并,比如第1块和第2块就不能合并。

      2. 碎片问题:一个连续的内存中仅仅一个页面被占用,导致整块内存区都不具备合并的条件

      3. 浪费问题:伙伴算法只能分配2的幂次方内存区,当需要8K(2页)时,好说,当需要9K时,那就需要分配16K(4页)的内存空间,但是实际只用到9K空间,多余的7K空间就被浪费掉。

      4. 算法的效率问题: 伙伴算法涉及了比较多的计算还有链表和位图的操作,开销还是比较大的,如果每次2^n大小的伙伴块就会合并到2^(n+1)的链表队列中,那么2^n大小链表中的块就会因为合并操作而减少,但系统随后立即有可能又有对该大小块的需求,为此必须再从2^(n+1)大小的链表中拆分,这样的合并又立即拆分的过程是无效率的。

  

    Linux针对大内存的物理地址分配,采用伙伴算法,如果是针对小于一个page的内存,频繁的分配和释放,有更加适宜的解决方案,如slab和kmem_cache等,这不在本文的讨论范围内。

 

      参考链接:伙伴算法剖析(原创)

                   伙伴系统算法

                   Buddy伙伴算法

Linux 伙伴算法简介的更多相关文章

  1. Linux伙伴算法

    Linux内存管理伙伴算法 伙伴算法 Linux内核内存管理的任务包括: 遵从CPU的MMU(Memory Management Unit)机制 合理.有效.快速地管理内存 实现内存保护机制 实现虚拟 ...

  2. 内存管理算法--Buddy伙伴算法【转】

    转自:http://blog.csdn.net/orange_os/article/details/7392986 Buddy算法的优缺点: 1)尽管伙伴内存算法在内存碎片问题上已经做的相当出色,但是 ...

  3. PageRank 算法简介

    有两篇文章一篇讲解(下面copy)< PageRank算法简介及Map-Reduce实现>来源:http://www.cnblogs.com/fengfenggirl/p/pagerank ...

  4. 伙伴算法与slab算法

    伙伴算法: 1.将空闲页面分为m个组,第1组存储2^0个单位的内存块,,第2组存储2^1个单位的内存块,第3组存储2^2个单位的内存块,第4组存储2^3个单位的内存块,以此类推.直到m组. 2.每个组 ...

  5. 新一代 Linux 文件系统 btrfs 简介

    https://www.ibm.com/developerworks/cn/linux/l-cn-btrfs/ Btrfs 简介 文件系统似乎是内核中比较稳定的部分,多年来,人们一直使用 ext2/3 ...

  6. Linux C++ 开发简介

    主要介绍将Windows程序迁移到Linux系统相关知识 简介 Windows程序迁移到Linux系统可能需要修改很多代码, 既需要了解Linux平台的开发知识, 也需要了解Windows平台代码如何 ...

  7. webrtc 的回声抵消(aec、aecm)算法简介(转)

    webrtc 的回声抵消(aec.aecm)算法简介        webrtc 的回声抵消(aec.aecm)算法主要包括以下几个重要模块:1.回声时延估计 2.NLMS(归一化最小均方自适应算法) ...

  8. 内存管理算法--Buddy伙伴算法

    Buddy算法的优缺点: 1)尽管伙伴内存算法在内存碎片问题上已经做的相当出色,但是该算法中,一个很小的块往往会阻碍一个大块的合并,一个系统中,对内存块的分配,大小是随机的,一片内存中仅一个小的内存块 ...

  9. AES算法简介

    AES算法简介 一. AES的结构 1.总体结构 明文分组的长度为128位即16字节,密钥长度可以为16,24或者32字节(128,192,256位).根据密钥的长度,算法被称为AES-128,AES ...

随机推荐

  1. java中静态的代码块,静态变量,静态方法

    简单了解一下java虚拟机--jvm几个内存区域: 方法区:在java的虚拟机中有一块专门用来存放已经加载的类信息.常量.静态变量以及方法代码的内存区域, 常量池:常量池是方法区的一部分,主要用来存放 ...

  2. Android自定义窗口动画

    第一步,设置出现和消失的xml 1.在res/anim下创建enter_anim.xml,设置窗口出现的动画 <?xml version="1.0" encoding=&qu ...

  3. eclipse的scala环境搭建

    两种方法使eclipse安装scala环境(eclipse luna) 1.下载eclipse for scala IDE http://scala-ide.org/download/sdk.html ...

  4. 微信js获得签名signature

    服务器端: 1 获取微信js accessToken 备注:access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token. 开发者需要进行妥善保存.access_ ...

  5. [Java] 转:多线程 (并发)总结

    一概念 二创建多线程方法 三线程常用方法不完整可以自己查阅JDK文档 四线程的生命周期与转换 五同步 六竞争者消费者 七线程池 八JDK 线程工具 线程基础: 1. 创建 2. 状态切换 3. sle ...

  6. Android 高级UI设计笔记21:Android SegmentView(分段选择控件)

    1. 分段控制(SegmentView) 首先我们先看看什么是SegmentView的效果,如下: 分段控制这个View控件是ios7的分段控制,和QQ消息页面顶部的效果一样,android没有这个控 ...

  7. 程序员怎样在复杂代码中找 bug?(简单)

    分享下我的debug的经验 1. 优先解决那些可重现的,可重现的bug特别好找,反复调试测试就好了,先把好解决的干掉,这样最节约时间. 2. 对于某些bug没有头绪或者现象古怪不知道从哪里下手,找有经 ...

  8. ubuntu下安装redis

    (1)进去 /usr/local目录下   cd /usr/local  若没有local这个文件夹则创建一个    sudo mkdir /usr/local     sudo chmod 777 ...

  9. [UML]UML之开篇

    前言 大学时,学习软件工程时,学到了UML,由于当时接触项目太少,认识不清,再加上毕业后一直忙于coding,很少有时间去真正的认识和学习UML. 现在感觉有必要去回头看看这些东西啦. 什么是UML ...

  10. C++中模板函数或模板类中关键词class和typename

    ##区别 基本上来说,class和typename几乎没有区别.在可以使用class的地方都可以使用typename,在使用typename的地方也几乎可以使用class. 可以看出我加黑了两个子:几 ...