内存管理算法--buddy伙伴算法

sky sky     2022-10-09     670

关键词:

转自:http://blog.csdn.net/orange_os/article/details/7392986

Buddy算法的优缺点:

1)尽管伙伴内存算法在内存碎片问题上已经做的相当出色,但是该算法中,一个很小的块往往会阻碍一个大块的合并,一个系统中,对内存块的分配,大小是随机的,一片内存中仅一个小的内存块没有释放,旁边两个大的就不能合并。
技术分享图片
2)算法中有一定的浪费现象,伙伴算法是按2的幂次方大小进行分配内存块,当然这样做是有原因的,即为了避免把大的内存块拆的太碎,更重要的是使分配和释放过程迅速。但是他也带来了不利的一面,如果所需内存大小不是2的幂次方,就会有部分页面浪费。有时还很严重。比如原来是1024个块,申请了16个块,再申请600个块就申请不到了,因为已经被分割了。

3)另外拆分和合并涉及到 较多的链表和位图操作,开销还是比较大的。

Buddy(伙伴的定义):

这里给出伙伴的概念,满足以下三个条件的称为伙伴:
1)两个块大小相同;
2)两个块地址连续;
3)两个块必须是同一个大块中分离出来的;

Buddy算法的分配原理:

假如系统需要4(2*2)个页面大小的内存块,该算法就到free_area[2]中查找,如果链表中有空闲块,就直接从中摘下并分配出去。如果没有,算法将顺着数组向上查找free_area[3],如果free_area[3]中有空闲块,则将其从链表中摘下,分成等大小的两部分,前四个页面作为一个块插入free_area[2],后4个页面分配出去,free_area[3]中也没有,就再向上查找,如果free_area[4]中有,就将这16(2*2*2*2)个页面等分成两份,前一半挂如free_area[3]的链表头部,后一半的8个页等分成两等分,前一半挂free_area[2]
的链表中,后一半分配出去。假如free_area[4]也没有,则重复上面的过程,知道到达free_area数组的最后,如果还没有则放弃分配。

技术分享图片

 

Buddy算法的释放原理:

内存的释放是分配的逆过程,也可以看作是伙伴的合并过程。当释放一个块时,先在其对应的链表中考查是否有伙伴存在,如果没有伙伴块,就直接把要释放的块挂入链表头;如果有,则从链表中摘下伙伴,合并成一个大块,然后继续考察合并后的块在更大一级链表中是否有伙伴存在,直到不能合并或者已经合并到了最大的块(2*2*2*2*2*2*2*2*2个页面)。

技术分享图片

整个过程中,位图扮演了重要的角色,如图2所示,位图的某一位对应两个互为伙伴的块,为1表示其中一块已经分配出去了,为0表示两块都空闲。伙伴中无论是分配还是释放都只是相对的位图进行异或操作。分配内存时对位图的
是为释放过程服务,释放过程根据位图判断伙伴是否存在,如果对相应位的异或操作得1,则没有伙伴可以合并,如果异或操作得0,就进行合并,并且继续按这种方式合并伙伴,直到不能合并为止。


Buddy内存管理的实现:

提到buddy 就会想起linux 下的物理内存的管理 ,这里的memory pool 上实现的 buddy 系统

和linux 上按page 实现的buddy系统有所不同的是,他是按照字节的2的n次方来做block的size

实现的机制中主要的结构如下:

整个buddy 系统的结构:

struct mem_pool_table

{

#define MEM_POOL_TABLE_INIT_COOKIE (0x62756479)

uint32 initialized_cookie; /* Cookie 指示内存已经被初始化后的魔数,  如果已经初始化设置为0x62756479*/

uint8 *mem_pool_ptr;/* 指向内存池的地址*/

uint32 mem_pool_size; /* 整个pool 的size,下面是整个max block size 的大小*/

uint32 max_block_size; /* 必须是2的n次方,表示池中最大块的大小*/   
boolean assert_on_empty; /* 如果该值被设置成TRUE,内存分配请求没有完成就返回 并输出出错信息*/
 uint32 mem_remaining; /* 当前内存池中剩余内存字节数*/                                              
uint32 max_free_list_index; /* 最大freelist 的下标,*/
struct mem_free_hdr_type     *free_lists[MAX_LEVELS];/* 这个就是伙伴系统的level数组*/

#ifdef FEATURE_MEM_CHECK
uint32 max_block_requested;
  uint32 min_free_mem; /* 放mem_remaining */
#endif /* FEATURE_ONCRPC_MEM_CHECK*/
};

 

这个结构是包含在free node 或alloc node 中的结构:

其中check 和 fill 都被设置为某个pattern
用来检查该node 的合法性
#define MEM_HDR_CHECK_PATTERN ((uint16)0x3CA4)
#define MEM_HDR_FILL_PATTERN ((uint8)0x5C)


typedef struct  tagBuddyMemBlockHeadType

{

    mem_pool_type pool; /*回指向内存池*/

    uint16 check;

    uint8 state; /* bits 0-3 放该node 属于那1级 bit 7 如果置1,表示已经分配(not free)

    uint8 fill;

} BUDDY_MEM_BLOCK_HEAD_TYPE;


这个结构就是包含node 类型结构的 free header 的结构:

typedef struct  tagBuddyMemHeadType

{

    mem_node_hdr_type hdr;

    struct mem_free_hdr_type * pNext;   /* next,prev,用于连接free header的双向 list*/

    struct mem_free_hdr_type * pPrev;

} mem_free_hdr_type;

这个结构就是包含node 类型结构的 alloc header 的结构:
已分配的mem 的node 在内存中就是这样表示的

  1. typedef struct mem_alloc_hdr_type
  2. {
  3.    mem_node_hdr_type hdr;

  4. #ifdef FEATURE_MEM_CHECK_OVERWRITE
  5.    uint32     in_use_size;
  6. #endif

  7. } mem_alloc_hdr_type;

其中用in_use_size 来表示如果请求分配的size 所属的level上实际用了多少
比如申请size=2000bytes, 按size to level 应该是2048,实际in_use_size
为2000,剩下48byte 全部填充为某一数值,然后在以后free 是可以check
是否有overwite 到着48byte 中的数值,一般为了速度,只 检查8到16byte

另外为什么不把这剩下的48byte 放到freelist 中其他level 中呢,这个可能
因为本来buddy 系统的缺点就是容易产生碎片,这样的话就更碎了

关于free or alloc node 的示意图:

假设

最小块为2^4=16,着是由mem_alloc_hdr_type (12byte)决定的, 实际可分配4byte

如果假定最大max_block_size =1024,

如果pool 有mem_free_hdr_type[0]上挂了两个1024的block node

上图是free node, 下图紫色为alloc node

技术分享图片


接下来主要是buddy 系统的操作主要包括pool init , mem alloc ,mem free

pool init :
 1. 将实际pool 的大小去掉mem_pool_table 结构大小后的size 放到
     mem_pool_size, 并且修改实际mem_pool_ptr指向前进mem_pool_table
     结构大小的地址
 2.  接下来主要将mem_pool_size 大小的内存,按最大块挂到free_lists 上
    level 为0的list 上,然后小于该level block size 部分,继续挂大下一
    级,循环到全部处理完成  (感觉实际用于pool的size ,应该为减去
    mem_pool_table 的大小,然后和最大块的size 对齐,这样比较好,
    但没有实际测试过)
   
   
mem alloc:
    这部分相当简单,先根据请求mem的size ,实际分配时需要加上mem_alloc_hdr_type
这12byte ,然后根据调整后的size,计算实际应该在那个 level上分配,如果有相应级
很简单,直接返回,如果没有,一级一级循环查找,找到后,把省下的部分,在往下一级
一级插入到对应级的freelist 上

mem free:
     其中free 的地址,减去12 就可以获得mem_alloc_hdr_type 结构
     然后确定buddy 在该被free block 前,还是后面, 然后合并buddy,
     循环寻找上一级的buddy ,有就再合并,只到最大block size 那级



关于这个算法,在<<The Art  of Computer Programming>> vol 1,的
动态存储分配中有描述,对于那些只有OSAL 的小系统,该算法相当有用

深入理解伙伴算法及其改进

...然后谈谈自己的理解。 体会  Linux操作系统主要的内存分配算法是伙伴系统(Buddy算法),机制是按照2的幂次方进行分块,然后根据需求分配差不多的内存块给使用者,伙伴系统是 查看详情

linux内核内存管理算法buddy和slab

...现在需要申请一个连续的5个页框。这个时候,在这段内存上不能找到连续的5个空闲的页框,就会去另一段内存上去寻找5个连续的页框,这样子,久而久之就形成了页框的浪费。为了避免出现这种情况,Linux内... 查看详情

buddy(伙伴)系统分配器之分配page(代码片段)

Buddy分配器是按照页为单位分配和释放物理内存的,在Zone那一节文章中freearea就是通过buddy分配器来管理的。buddy分配器将空闲页面按照order的大小分配挂到不同的order链表中。比如order为0的链表下就挂载着大小为1个page的空闲... 查看详情

linux内存从0到1学习笔记(6.8,物理内存初始化之buddy伙伴系统)

写在前面在linux启动的那一刻,内存管理就已经开始了。在内核中,实现物理内存管理的allocator包括:初始化阶段物理内存管理memblock连续物理内存管理buddy非连续物理内存管理vmallocallocator小块物理内存管理slaballocator在系统初始... 查看详情

伙伴算法的实现-分配页框

...用伙伴算法的入口函数buffered_rmqueue()。Linux内核管理物理内存有三种方式,其一就是经典的伙伴算法。但是伙伴算法分配物理内存的基本单位是页框,因此内核又引入了slab机制,基于此机制实现的物理内存分配器可以快速有效的... 查看详情

12linux的伙伴系统和slab分配器

伙伴系统: buddy物理内存页面管理算法,最先源自Sun公司的Solaris操作系统;Linux后来也引入了伙伴系统;表示一个物理内存页面:Linux定义了一个page结构体,大量使用了c的union联合体定义结构字段,其大小取决于结构体里面... 查看详情

内存管理:一文读懂linux内存组织结构及页面布局

参考技术A1、内存是什么?1)内存又称主存,是CPU能直接寻址的存储空间,由半导体器件制成;2)内存的特点是存取速率快,断电一般不保存数据,非持久化设备;2、内存的作用1)暂时存放cpu的运算数据2)硬盘等外部存储器交换的... 查看详情

[linux]内存原来还有这么多事儿,3个版本迭代说清楚内存的故事|内存管理,内存池,slab,伙伴算法,tcmalloc,jemalloc

[linux]内存原来还有这么多事儿,3个版本迭代说清楚内存的故事|内存管理,内存池,slab,伙伴算法,tcmalloc,jemalloc专注于后台服务器开发,包含了C/C++,Linux,Nginx,ZeroMQ,MySQLÿ... 查看详情

好友分配算法 - 起始堆地址

】好友分配算法-起始堆地址【英文标题】:BuddyAllocationAlgorithm-BeginningHeapAddress【发布时间】:2013-10-3005:47:39【问题描述】:我目前正在尝试实现计算机编程艺术卷:1中描述的BuddyAllocator,它利用了给定数据块及其对应地址的重... 查看详情

jvm内存区域管理算法-垃圾回收算法

...C语言相比,最大的特点是编程人员无需过多的关心Java的内存分配和回收,因为所有这一切,Java的虚拟机都帮我们实现了。JVM的内存管理,大大降低了开发人员对内存管理的要求,也不容易出现C语言中的内存泄漏和溢出。但一... 查看详情

(王道408考研操作系统)第三章内存管理-第二节2:页面置换算法2

上接:(王道408考研操作系统)第三章内存管理-第二节2:页面置换算法1文章目录一:时钟置换算法(CLOCK)(1)简单时钟置换算法(2)改进型时钟置换算法二:页面置换算法总结一:时钟置换算法(CLOCK)(1)简单时钟置换算法... 查看详情

(王道408考研操作系统)第三章内存管理-第二节3:页面置换算法2

上接:(王道408考研操作系统)第三章内存管理-第二节2:页面置换算法1文章目录一:时钟置换算法(CLOCK)(1)简单时钟置换算法(2)改进型时钟置换算法二:页面置换算法总结一:时钟置换算法(CLOCK)(1)简单时钟置换算法... 查看详情

jvm自动内存管理:对象判定和回收算法

     查看详情

为什么新生代内存需要有两个survivor区?

...标记整理算法等。标记清除算法由于回收之后存在大量的内存碎片,存在效率和空间问题!为了解决效率问题,引出了复制算法!熟悉GC算法的小伙伴应该都看过周志明老师的《深入理解Java虚拟机》这本书。因此,这里不再讨论... 查看详情

jvm自动内存管理:对象判定和回收算法

可回收对象的判断方法1.引用计数算法2.可达性分析算法 引用计数算法给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使... 查看详情

操作系统-3.3-内存(动态分区分配算法&&基本分页存储管理详解)

文章目录操作系统-3.3-内存5.动态分区分配算法5.1首次适应算法5.2最佳适应算法5.3最坏适应算法5.4邻近适应算法5.5总结6.基本分页存储管理6.1把“固定分区分配“改造为”非连续分配版本“6.2分页存储管理的基本管理6.3如何实现地... 查看详情

rt-thread--内存管理(代码片段)

内存管理的功能特点RT-Thread操作系统在内存管理上,根据上层应用及系统资源的不同,有针对性地提供了不同的内存分配管理算法。总体上可分为两类:内存堆管理与内存池管理,而内存堆管理又根据具体内存设备划分为三种情... 查看详情

android内存管理

垃圾内存回收算法在垃圾内存回收算法中,我们常见的垃圾回收算法有引用计数法(ReferenceCounting)、标注并清理(MarkandSweepGC)、拷贝(CopyingGC)和逐代回收(GenerationalGC)等算法。引用计数回... 查看详情