☰
page_alloc rmqueue_buddy
2026/10/11 1:47:35 网站建设 项目流程

rmqueue_buddy()是页面分配路径中绕过 PCP 缓存、直接访问伙伴系统的函数。它通常在rmqueue()中作为 PCP 缓存分配失败后的后备路径被调用。

核心作用与定位

它的核心职责是在持有zone->lock保护的前提下,从伙伴系统的空闲链表中取出一个页块。与__rmqueue()相比,它多了一层封装:处理 HIGHATOMIC 保留区、循环重试坏页,并更新分配统计。

核心逻辑

根据 Mel Gorman 在 2022 年将其从rmqueue()中拆分出来的补丁,它的典型实现如下:

static __always_inline struct page *rmqueue_buddy(struct zone *preferred_zone, struct zone *zone, unsigned int order, unsigned int alloc_flags, int migratetype) { struct page *page; unsigned long flags; do { page = NULL; spin_lock_irqsave(&zone->lock, flags); /* * order-0 请求可能因非 CMA 分配上下文而跳过 pcplist。 * HIGHATOMIC 区保留给高阶原子分配,order-0 请求应跳过它。 */ if (order > 0 && alloc_flags & ALLOC_HARDER) { page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC); if (page) trace_mm_page_alloc_zone_locked(page, order, migratetype); } if (!page) { page = __rmqueue(zone, order, migratetype, alloc_flags); if (!page) { spin_unlock_irqrestore(&zone->lock, flags); return NULL; } } __mod_zone_freepage_state(zone, -(1 << order), get_pcppage_migratetype(page)); spin_unlock_irqrestore(&zone->lock, flags); } while (check_new_pages(page, order)); __count_zid_vm_events(PGALLOC, page_zonenum(page), 1 << order); zone_statistics(preferred_zone, zone, 1); return page; }

逐步说明

1. 获取zone->lock

spin_lock_irqsave(&zone->lock, flags);

伙伴系统的空闲链表由zone->lock保护,所有操作必须在此锁下进行。

2. HIGHATOMIC 优先(仅限高阶ALLOC_HARDER)

if (order > 0 && alloc_flags & ALLOC_HARDER) { page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC);
  • MIGRATE_HIGHATOMIC是为高阶原子分配(如GFP_ATOMIC且 order > 0)预留的迁移类型。

  • 仅在ALLOC_HARDER标志(高优先级分配)且order > 0时尝试,确保这些稀缺资源不被低优先级请求消耗。

3. 常规伙伴分配

page = __rmqueue(zone, order, migratetype, alloc_flags);
  • 调用前面讲过的__rmqueue(),它内部会依次尝试__rmqueue_smallest、__rmqueue_claim、__rmqueue_steal等策略。

4. 更新空闲页统计

__mod_zone_freepage_state(zone, -(1 << order), get_pcppage_migratetype(page));
  • 从 zone 的空闲页计数中减去刚分配的页数。

  • 迁移类型从页的private字段(get_pcppage_migratetype)获取,可能因 fallback 而不同于请求类型。

5. 坏页检查与重试

} while (check_new_pages(page, order));
  • 分配到的页需要经过check_new_pages()校验。

  • 如果发现坏页(调试配置下),释放锁后重新尝试分配,避免把损坏的页交给调用方。

6. 更新 VM 事件与 NUMA 统计

__count_zid_vm_events(PGALLOC, page_zonenum(page), 1 << order); zone_statistics(preferred_zone, zone, 1);
  • PGALLOC:记录页分配事件。

  • zone_statistics:更新 NUMA 命中/未命中统计。

在分配路径中的位置

rmqueue_buddy()被rmqueue()在 PCP 路径无法满足时调用:

rmqueue() ├── if (pcp_allowed_order(order)) { │ page = rmqueue_pcplist(...); // 尝试 PCP │ if (page) return page; │ } └── page = rmqueue_buddy(preferred_zone, zone, order, alloc_flags, migratetype); // ← 这里

总结

rmqueue_buddy()是伙伴系统的直接分配入口:它在zone->lock保护下,优先处理高阶ALLOC_HARDER请求的MIGRATE_HIGHATOMIC保留区,然后通过__rmqueue()执行多级降级分配,更新空闲页统计,并在坏页时循环重试。它是rmqueue()在 PCP 缓存无法满足请求时的后备路径,确保了分配在绕过 CPU 缓存后仍能安全、准确地完成。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询