Linux内存管理: Buddy Allocator System

网上说的对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;
};

Linux内存管理: Buddy Allocator System_第1张图片

这里提两个问题

  1. 放到free_area里边的内存页是怎么分配来的?
    1) 在zone里边找到free block
    2) 为了找到合适的block,需要根据order的值,在对应的list里边开始找
    3) 如果没有的话,就按order递增方向再寻找。找到的话,分配完把剩余的割开放到对应的list里
    __rmqueue( )实现free block寻找。需要传zone和order作为参数。会返回第一个page的地址

  2. 通过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当中进行分配

你可能感兴趣的:(linux,内存管理)