3. 伙伴系统数据结构

伙伴系统的核心数据结构围绕三个层次展开:区域(zone)、迁移类型(migrate type) 和 阶(order)。理解 free_area 数组、struct page、zone 结构体以及 buddy bitmap 的协作关系,是掌握伙伴系统内存分配与回收机制的基础。

3.1 zone 结构体:内存区域的“总控室”

每个 struct zone 描述一个物理内存区域(如 ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM)。伙伴系统在该区域内的所有操作,都通过 zone 结构体中的 free_area 数组和 pageblock_flags 等字段进行管理。

字段 类型 说明
free_area[MAX_ORDER] struct free_area 按阶(order)组织的空闲页块链表数组,每个元素对应一个阶
pageblock_flags unsigned long * 页块迁移类型位图,用于防治内存碎片
managed_pages unsigned long 伙伴系统管理的物理页总数
watermark[NR_WMARK] unsigned long 内存水位线(min/low/high),控制回收触发时机
关键点:zone 结构体中的 free_area 数组是伙伴系统分配器的直接操作对象,而 pageblock_flags 位图则用于实现迁移类型分组,两者共同决定了内存分配的行为和碎片防治效果。

3.2 free_area 数组:按阶组织的空闲页块链表

free_area[MAX_ORDER] 是伙伴系统的核心索引结构。数组下标 order 表示页块的阶(0 到 MAX_ORDER-1,通常 MAX_ORDER=11),每个元素包含一个链表头和一个位图。

// include/linux/mmzone.h
struct free_area {
    struct list_head    free_list[MIGRATE_TYPES];
    unsigned long       *map;   // buddy bitmap
};
  • free_list[MIGRATE_TYPES]:每个迁移类型对应一个链表,链入该阶下该迁移类型的空闲页块。
  • map:指向 buddy bitmap 的指针,用于快速判断两个页块是否为伙伴。
设计意图:将空闲页块按阶和迁移类型双重分类,使得分配时能快速定位到满足大小和碎片防治要求的页块,避免遍历整个空闲链表。

3.3 struct page:物理页的“身份证”

每个物理页框对应一个 struct page 实例,伙伴系统通过该结构体管理页面的状态、引用计数和链表指针。

字段 说明
flags 页面标志位,如 PG_buddy(标记该页属于伙伴系统)、PG_private 等
_refcount 引用计数,用于页面共享和释放判断
lru 链表节点,用于将页面链入 free_area 的 free_list 或 LRU 链表
private 当页面作为伙伴系统页块时,存储该页块的 order 值
zone 指向所属 zone 的指针,用于快速定位区域信息
注意:当页面被伙伴系统管理时,PG_buddy 标志位被置位,此时 lru 字段用于链入 free_list,private 字段存储该页块所属的阶。一旦页面被分配出去,这些字段的语义会发生变化。

3.4 buddy bitmap:伙伴关系判定位图

每个 free_area 元素包含一个位图 map,用于快速判断两个连续页块是否为伙伴。位图的每一位对应一对候选伙伴页块。

  • 位图大小:对于阶 order,位图位数 = zone 总页数 >> (order + 1)。
  • 位图含义:某位为 1 表示该对伙伴中至少有一个页块是空闲的;为 0 表示两个页块都已分配或都不存在。
  • 作用:在释放页块时,通过位图快速判断伙伴是否空闲,从而决定是否合并。
// 判断两个页块是否为伙伴的简化逻辑
static inline unsigned long find_buddy(unsigned long page_idx, unsigned int order) {
    return page_idx ^ (1 << order);
}

// 通过 buddy bitmap 检查伙伴是否空闲
static inline int buddy_is_free(struct page *buddy, unsigned int order) {
    unsigned long buddy_idx = page_to_pfn(buddy);
    unsigned long bit = buddy_idx >> (order + 1);
    return test_bit(bit, zone->free_area[order].map);
}

3.5 数据结构关系总览

下图展示了 zone、free_area、struct page 和 buddy bitmap 之间的协作关系:

伙伴系统核心数据结构关系图 struct zone free_area[MAX_ORDER] pageblock_flags managed_pages watermark[NR_WMARK] ... (其他字段) free_area[MAX_ORDER] free_area[0] (order=0, 4KB) free_area[1] (order=1, 8KB) free_area[2] (order=2, 16KB) ... free_area[10] (order=10, 4MB) struct page flags (PG_buddy 等) _refcount lru (链表节点) private (存储 order) zone (指向所属 zone) buddy bitmap (map) 每个 free_area 元素包含一个位图 位图位数 = 总页数 >> (order+1) 用于快速判断伙伴是否空闲 pageblock_flags 迁移类型位图 每个 pageblock 对应 4 位 标记页块的迁移类型 free_list[MIGRATE_TYPES] MIGRATE_UNMOVABLE MIGRATE_RECLAIMABLE MIGRATE_MOVABLE / MIGRATE_CMA 等

3.6 数据结构协作流程

  1. 分配请求到达:内存分配器根据请求大小计算所需阶 order,并确定迁移类型 migratetype。
  2. 定位 free_area:从 zone 的 free_area[order] 开始查找,若该阶有空闲页块,则从对应迁移类型的 free_list[migratetype] 中取出。
  3. 无空闲则升级:若当前阶无空闲,则向高阶查找,找到后分裂页块,并将低阶页块插入对应 free_list。
  4. 更新位图:分配或释放页块时,同步更新 buddy bitmap 对应位,确保伙伴关系判定准确。
  5. 释放与合并:释放页块时,通过 buddy bitmap 检查伙伴是否空闲,若空闲则合并为高阶页块,并递归向上合并。
性能关键点:buddy bitmap 的位操作和 free_list 的链表操作都是 O(1) 或 O(log N) 级别,保证了伙伴系统分配和释放的高效性。迁移类型分组则通过将不同生命周期的页面隔离,有效减少了长期不可移动页面导致的碎片问题。