内存碎片通常分为外部碎片和内部碎片两种。外部碎片指内存全部是分散小块而无法满足大块内存申请的需求;内部碎片指申请到一页或几页内存后只使用了部分内存造成其他内存浪费。linux因此引入了伙伴算法解决外部碎片问题,slab方式解决内部碎片问题,本章主要讲解伙伴算法,下一章讲解slab层。
伙伴算法是一个结合了2的方幂分配器与空闲缓冲区合并技术的分配方案。内存被分成了含有很多页面的大块,每一个块都是2的方幂个页面大小。如果找不到想要的块,上一级的大块会被分成两部分,这两部分彼此就成了伙伴,其中一半被用来分配而另外一半空闲放入本级空闲链表,这些块会继续被二分直至产生一个所需大小的块。当一个块被最终释放时,其伙伴将被检测出来,如果空闲就合并两者。
1、管理空闲块
伙伴系统维护空闲页面所组成的块,每一块都是2的方幂个页面。每个管理区struct zone中struct free_area free_area[MAX_ORDER]数组就用于管理伙伴算法:
#define MAX_ORDERCONFIG_FORCE_MAX_ZONEORDER =11
struct free_area {
struct list_head free_list[MIGRATE_TYPES]; //空闲链表
unsigned long nr_free; //空闲链表个数
};
MAX_ORDER = 11意味这最大支持的连续物理快2^11* 4K = 8M。其示意图如下:
由于本arm体系结构只有一个ZONE_NORMAL管理区,在内核伙伴算法初始化完成后,其伙伴算法的空闲链表数目如下:
free_area:0, nr_free:2
free_area:1, nr_free:0
free_area:2, nr_free:2
free_area:3, nr_free:2
free_area:4, nr_free:1
free_area:5, nr_free:2
free_area:6, nr_free:2
free_area:7, nr_free:2
free_area:8, nr_free:1
free_area:9, nr_free:1
free_area:10, nr_free:1
free_area:11, nr_free:29
2、操作函数
(1)alloc_page
#define alloc_page(gfp_mask)alloc_pages(gfp_mask, 0)
分配一个页面并返回一个页地址
(2)alloc_pages
#define alloc_pages(gfp_mask, order) \
alloc_pages_node(numa_node_id(), gfp_mask, order)
分配2^order数量的页面并返回第一个页地址
(3)page_address
该函数返回一个指针,指向给定物理页当前所在的逻辑地址
(4)get_zeroed_page
unsigned long get_zeroed_page(gfp_tgfp_mask)
分配一个页面,对其初始化成0值,返回一个虚拟地址
(5)__get_free_pages
unsigned long __get_free_pages(gfp_tgfp_mask, unsigned int order);
分配2^order数量的页面并返回一个虚拟地址
(6)__get_free_page
#define __get_free_page(gfp_mask) \
__get_free_pages((gfp_mask), 0)
分配一个页面并返回虚拟地址
(7)__free_pages
void __free_pages(struct page *page,unsigned int order);
从给定页面中释放2^order页面大小的块
(8)free_pages
void free_pages(unsigned long addr,unsigned int order);
从给定虚拟地址空间释放2^order页面大小的块
(9)__free_page
#define __free_page(page)__free_pages((page), 0)
从给定页面中释放1个页面
(10)free_page
#define free_page(addr) free_pages((addr),0)
从给定的虚拟地址空间释放一个页面
释放需要注意的是:只能释放当初申请的页,如果传递了错误的参数,可能会导致系统崩溃。