网上说的对buddy system的简单说明。
Divides memory into partitions to try to satisfy a memory request as suitably as possible
- Splits memory into halves to try to give a best-fit
- Invented in 1963, Harry Markowitz
Effectively reduces external fragmentation with small compaction overhead
buddy allocator system算法
buddy allocator是用于分配大于4KB,也就是一个page以上的内存分配时使用的分配器。
buddy allcator除了分配以page为单位的内存之外,另外的功能就是管理分配page之后剩余的page frame,避免出现太多内存碎片。
buddy allocator在每个zone都会单独进行page的管理。
一般都会有11个list,每个list代表一个由1,2,4,8,…,1024个连续的page组成的group。
每组都会被管理在zone数据结构下面的free_area
struct zone {
struct free_area free_area[MAX_ORDER];
}
//MAX_ORDER表明当前组里边page个数最多是2的多少个阶乘。
//可以在mmzone.h文件里边找到如下定义
//#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER
//比如CONFIG_FORCE_MAX_ZONEORDER是11的话,最大的组里边有2的11次方个page
struct free_area {
struct list_head free_list[MIGRATE_TYPES];
unsigned long nr_free;
};
这里提两个问题
放到free_area里边的内存页是怎么分配来的?
1) 在zone里边找到free block
2) 为了找到合适的block,需要根据order的值,在对应的list里边开始找
3) 如果没有的话,就按order递增方向再寻找。找到的话,分配完把剩余的割开放到对应的list里
__rmqueue( )实现free block寻找。需要传zone和order作为参数。会返回第一个page的地址
通过buddy system从free_area获取合适大小的page个数,使用完之后释放是否还是通过buddy system管理?
在释放的时候,会寻找周围是否有可以合并的buddy。进行合并。放到free list中。
__free_pages_bulk()负责释放一组page并按照buddy system的策略合并page。
参数如下:
1) page: 一组内存当中第一个page的地址
2) zone :
3) order:
通过adb可以看到当前的buddyinfo。
这里可以看到每个部分的Node号(如果不是NUMA的话,一般都是0),zone的名字。
然后从左到右是表示2的0次方到2的10次方page大小内存的块的个数。
比如Normal最后的59,表示Normal里边有59个2^10个page大小的连续的内存。
buddy allocator system函数接口
函数说明
struct page * alloc_pages (gfp_t gfp_mask, unsigned int order);
找到2^order个连续的page大小的内存,返回第一个page的地址。
alloc_page(gfp_mask)
取一个page,返回这个page的地址
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
使用alloc_pages来获得2^order个page大小的第一个page的地址。
分配成功的话__get_free_pages会返回2^order个page大小的连续的,注意是连续的物理内存的虚拟地址!!
这个可以看一下__get_free_pages()函数的实现,来反证上面说的page连续的问题。
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
{
struct page *page;
page = alloc_pages(gfp_mask, order);//找到了2^order个连续的page大小的内存。返回第一个page。
if (!page)
return 0;
return (unsigned long) page_address(page);
//返回这个page对应的地址。要是2^order个page不连续的话,这种实现肯定是有问题的。
}
__get_free_page(gfp_mask)
和上面__get_free_pages差不多,只是返回一个page的虚拟地址。
gfp_mask
如果设置了__GFP_DMA flag
只在ZONE_DMA区域分配page(ARM系统没有这个~~)
如果没有设置__GFP_HIGHMEM flag
没有设置__GFP_HIGHMEM,则page会在ZONE_NORMAL或者ZONE_DMA申请分配。
如果设置了__GFP_HIGHMEM flag
设置了__GFP_HIGHMEM flag,则paga会在ZONE_NORMAL或者ZONE_DMA或者ZONE_HIGHMEM当中进行分配