chipset: MSM8X25Q
Codebase: Android4.1
Kernel: 3.4.0
基本概念:
关于伙伴系统算法的原理还是比较好理解的,这里不作复数。直接看下关键数据结构。
struct zone {
~~snip
struct free_area free_area[MAX_ORDER]; //每一阶以一个元素保存,平台最大11阶。
~~snip
};
可以看到每个zone都有它自己的free_area,效果如下图:
相关信息可以从/proc/buddyinfo读取到,各个值含义依次为节点号, zone类型,后面的值表示从0阶开始各个阶空闲的页数:
#cat/proc/buddyinfo
Node0, zone Normal 70 27
33 148 47 13 7 2
2 1 1
Node0, zone HighMem 0 1 1 2 2 0 0 0
0 0 0
struct free_area {
/*同样阶数保存到一个链表上 */
struct list_head free_list[MIGRATE_TYPES];
/*当前空闲页块的数目。*/
unsigned long nr_free;
};
效果如下图:

MIGRATE_TYPES是作为反碎片的一种机制,专有名叫迁移类型。大概的原理就是将伙伴系统的内存页分为几种类型,有可移动,不可移动,可回收等,同一类型的页放在一个区域,如不可回收的页不能放在可移动类型区域,这样对可以移动区域,伙伴系统就可以回收了。
enum {
MIGRATE_UNMOVABLE,
MIGRATE_RECLAIMABLE,
MIGRATE_MOVABLE,
MIGRATE_PCPTYPES, /* the number of types on the pcp lists */
MIGRATE_RESERVE = MIGRATE_PCPTYPES,
#ifdef CONFIG_CMA
MIGRATE_CMA,
#endif
MIGRATE_ISOLATE, /* can't allocate from here */
MIGRATE_TYPES
};
MIGRATE_UNMOVABLE:不可移动页,在内存中有固定位置,不能移动。核心内核分配的大部分内存属于此类。
MIGRATE_RECLAIMABLE:可回收页,不能移动,但能删除。Kswapd内核线程会操作次区域。
MIGRATE_MOVABLE:可移动又可回收页,用户空间程序使用此类,通过页表映射实现,如果应用程序虚拟地址空间有变化,只要变化页表就可以了。
MIGRATE_RESERVE: 当系统内存相当少而且比较紧急时,才用到此区域。
MIGRATE_CMA:这个是为了避免预留大块内存实现的,当需要大块内存的时候如audio/camera等,它可以被使用;当小内存申请需要时,它也可以被使用,避免了pmem/ion的弊端,不过似乎要基于DMA。后面打算用一篇文章来分析cma.
MIGRATE_ISOLATE: NUMA系统上使用,我们用UMA,不管它。
当某个迁移类型的内存不足时,会向另外一个迁移类型去要内存。这个跟zone的申请机制很像!下面结构规定了当前迁移类型不够时下一个使用的类型,如MIGRATE_UNMOVABLE的使用顺序是: MIGRATE_RECLAIMABLE -> MIGRATE_RECLAIMABLE -> MIGRATE_MOVABLE -> MIGRATE_RESERVE.static int fallbacks[MIGRATE_TYPES][4] = {
[MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },
[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },
#ifdef CONFIG_CMA
[MIGRATE_MOVABLE] = { MIGRATE_CMA, MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
[MIGRATE_CMA] = { MIGRATE_RESERVE }, /* Never used */
#else
[MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
#endif
[MIGRATE_RESERVE] = { MIGRATE_RESERVE }, /* Never used */
[MIGRATE_ISOLATE] = { MIGRATE_RESERVE }, /* Never used */
};
迁移类型的信息可以从/proc/pagetypeinfo读到:
#cat /proc/pagetypeinfo
Page block order: 10
Pages per block: 1024
Free pages count per migrate type at order 0 1 2 3 4
5 6 7 8 9 10
Node 0, zone Normal, type Unmovable 1 1 2 0 1
0 1 1 1 0 0
Node 0, zone Normal, type Reclaimable 8 18 3 0 0
0 0 0 0 0 0
Node 0, zone Normal, type Movable 1 1 0 115 46
12 5 0 0 0 0
Node 0, zone Normal, type Reserve 0 0 0 1 0
1 1 1 1 1 1
Node 0, zone Normal, type Isolate 0 0 0 0 0
0 0 0 0 0 0
Node 0, zone HighMem, type Unmovable 0 0 0 0 0
0 0 0 0 0 0
Node 0, zone HighMem, type Reclaimable 0 0 0 0 0
0 0 0 0 0 0
Node 0, zone HighMem, type Movable 0 1 0 0 0
0 0 0 0 0 0
Node 0, zone HighMem, type Reserve 0 0 1 2 2
0 0 0 0 0 0
Node 0, zone HighMem, type Isolate 0 0 0 0 0
0 0 0 0 0 0
Number of blocks type Unmovable Reclaimable Movable Reserve
Isolate
Node 0, zone Normal 13 8 178 2
0
Node 0, zone HighMem 1 0 14 1
0
初始化:
首先对伙伴系统相关数据结构初始化,有如下调用流程:
start_kernel -> setup_arch ->paging_init -> bootmem_init -> arm_bootmem_free ->
free_area_init_node ->init_currently_empty_zone -> zone_init_free_lists
static void __meminit zone_init_free_lists(struct zone *zone)
{
int order, t;
/