expand()是伙伴系统分配路径中的核心拆分函数。当从空闲链表取出的页块大于所需阶数时它负责将大块“切蛋糕”一样逐级拆分把不需要的部分重新放回低阶空闲链表最终只留下恰好满足请求的页块。核心作用与逻辑expand()的核心任务是在分配高阶页块时进行降级拆分。它的逻辑是从高阶high开始每次循环将阶数减一把拆分出的“后半块”页块重新标记为 Buddy 状态并加入到对应阶数的空闲链表中直到达到所需的低阶low。参数解析参数含义zone目标内存区域page页块首页low目标阶数实际需要的阶数high起始阶数从空闲链表取出的页块阶数migratetype页块的迁移类型核心流程1. 逐级拆分函数进入一个while (high low)循环每次迭代执行high--相当于降低一级 order 。然后计算size 1 high定位到拆分出的后半块页块page[size]。2. 标记与归还对拆分出的页块执行__add_to_free_list()或add_to_free_list()将其加入对应阶数的空闲链表并调用set_buddy_order()设置其 Buddy 状态和private字段中的 order 值 。3. 统计更新现代内核中expand()会累加拆分归还的页数nr_added最后统一调用account_freepages()更新统计避免了在循环中频繁更新 vmstat 的开销 。在分配路径中的位置它被__rmqueue_smallest()和__rmqueue_fallback()调用。调用链如下__rmqueue_smallest() └── 从 free_area[current_order] 取出页块 └── del_page_from_free_list() // 从链表移除 └── expand(zone, page, order, current_order, migratetype) // 拆分 └── 将剩余部分放回低阶链表只有current_order order时才需要拆分如果恰好相等expand()直接返回。关键设计点1. 拆分顺序影响 I/O 性能内核注释特别强调拆分时先分配低地址部分还是高地址部分会显著影响 IO 子系统的 sglist 合并效率因此这个顺序是经过经验测试固定的 。2. 尾插优化有补丁建议将拆分出的页块加入链表尾部而非头部这样它们不容易被立即再次分配从而有更大机会与后续释放的伙伴合并减少碎片 。3. 迁移类型的处理如果内核决定改变该页块的迁移类型如 fallback 时拆分出的剩余部分也会被放入新的迁移类型链表中 。总结expand()是伙伴系统的“切蛋糕”函数它从高阶空闲块中逐级拆分将不需要的部分重新标记为 Buddy 状态并归还到低阶链表最终只保留恰好满足请求大小的页块。它的拆分顺序、统计更新方式和链表插入策略都是影响伙伴系统碎片化和 IO 性能的关键细节。