
move_freepages_block()是反碎片机制中用于将一个 pageblock 内的空闲页从一种迁移类型迁移到另一种的封装函数。它建立在prep_move_freepages_block()和底层的move_freepages()之上主要服务于“偷页”steal_suitable_fallback和内存隔离场景。函数签名与定位static int move_freepages_block(struct zone *zone, struct page *page, int old_mt, int new_mt)它接收zone、一个 pageblock 内的page用于定位范围、旧迁移类型old_mt和新迁移类型new_mt返回实际移动的页数。它是steal_suitable_fallback()的核心执行步骤之一。核心逻辑封装层move_freepages_block()本身是prep_move_freepages_block()的“执行封装”——先调用prep_move_freepages_block()计算有效范围并统计然后调用底层的move_freepages()完成实际的链表移动static int move_freepages_block(struct zone *zone, struct page *page, int old_mt, int new_mt) { unsigned long start_pfn, end_pfn; int num_free, num_movable; /* * 计算 pageblock 范围处理 zone 边界截断 * 并统计空闲页/可移动页数量。 */ if (!prep_move_freepages_block(zone, page, start_pfn, end_pfn, num_free, num_movable)) return 0; /* * 执行实际的页块移动 * 遍历 [start_pfn, end_pfn] 内的空闲 Buddy 页 * 用 move_to_free_list() 将它们从 old_mt 链表移到 new_mt 链表。 */ return move_freepages(zone, start_pfn, end_pfn, old_mt, new_mt); }关键点prep_move_freepages_block()负责安全边界计算避免跨越 zone 边界操作无效 PFNmove_freepages()负责遍历并移动。如果范围计算失败如 pageblock 跨越 zone 边界直接返回 0不做任何移动。与move_freepages()的分工底层的move_freepages()是一个更通用的函数接受任意 pfn 范围不要求 pageblock 对齐遍历范围内的空闲页并执行迁移static int move_freepages(struct zone *zone, unsigned long start_pfn, unsigned long end_pfn, int old_mt, int new_mt) { for (pfn start_pfn; pfn end_pfn; ) { if (!pfn_valid_within(pfn)) { pfn; continue; } page pfn_to_page(pfn); if (!PageBuddy(page)) { pfn; continue; } order buddy_order(page); /* 只移动匹配旧迁移类型的空闲页 */ if (get_pcppage_migratetype(page) old_mt) move_to_free_list(page, zone, order, old_mt, new_mt); pfn (1 order); } ... }move_freepages_block()定位 pageblock 范围处理边界是steal_suitable_fallback的入口。move_freepages()执行实际的链表遍历和move_to_free_list()调用可用于任意范围。调用场景move_freepages_block()主要被steal_suitable_fallback()调用用于偷取整个 pageblocksteal_suitable_fallback() └── prep_move_freepages_block(...) // 统计空闲页/可移动页 └── if (值得偷整个 block) └── move_freepages_block(zone, page, block_type, start_type) // ← 这里 └── move_freepages(...) └── move_to_free_list(...) // 逐个页块迁移在steal_suitable_fallback()中当统计结果显示该 pageblock 内“空闲页 相似可移动页”超过一半时内核就会调用move_freepages_block()把整个 block 的空闲页移到请求类型并更新 pageblock 的迁移类型标记。此外它也用于内存隔离场景在unset_migratetype_isolate()中把隔离的页块移回目标类型链表。关键设计点1. 尾插优化move_to_free_list()内部使用list_move_tail()而非list_move()将移动的页放到链表尾部。这是 2020 年 David Hildenbrand 引入的优化目的是让刚移动的页如内存热插拔上线的页、隔离解除的页不会立即被分配走从而有更大机会与后续释放的伙伴合并减少碎片。2. Zone 边界与内存空洞prep_move_freepages_block()会处理两种边界情况zone 边界如果 pageblock 跨越 zone截断到 zone 范围内如果end_pfn超出 zone直接放弃操作。内存空洞pageblock 内可能包含未初始化的 PFN物理空洞move_freepages()用pfn_valid_within()检查并跳过避免操作无效的struct page导致崩溃。3. 返回移动页数返回值供steal_suitable_fallback()判断迁移是否“成功”如移动页数是否达到阈值也用于统计更新。总结move_freepages_block()是反碎片“偷页”策略的执行封装它先通过prep_move_freepages_block()安全计算 pageblock 的有效范围并统计空闲页再调用move_freepages()遍历范围内的空闲 Buddy 页用尾插方式将它们从旧迁移类型链表移到新类型链表返回实际移动的页数。它由steal_suitable_fallback()在“偷整个 block”时调用也用于内存隔离的解除是内核在迁移类型耗尽时从其他 pageblock 借页的关键执行环节。