alloc_pages_bulk_noprof()是内核提供的批量页面分配接口。它的核心设计目标是减少锁竞争和函数调用开销:通过一次持有 PCP(Per-CPU Pages)锁,连续分配多个页面填充到调用方提供的数组中,而不是反复调用单页分配接口。
函数签名与参数
unsigned long alloc_pages_bulk_noprof(gfp_t gfp, int preferred_nid, nodemask_t *nodemask, int nr_pages, struct page **page_array)
| 参数 | 含义 |
|---|---|
gfp | 分配标志 |
preferred_nid | 首选 NUMA 节点 |
nodemask | 允许的节点掩码 |
nr_pages | 请求分配的页数(数组长度) |
page_array | 输出数组,用于存放分配的页面指针 |
返回值:实际成功分配的页数。
核心特性与约束
1. 只填充 NULL 槽位
该接口的一个关键语义是:它只填充数组中为 NULL 的槽位,已经非 NULL 的槽位会被跳过。代码中通过nr_populated来跟踪已填充的槽位数量,并在水位检查中使用(nr_pages - nr_populated)来计算剩余需求。
2. 仅支持 order-0
alloc_pages_bulk_noprof()是专门为单页(order-0)分配设计的,不支持高阶分配。如果需要分配高阶页,必须使用__alloc_pages_noprof()等单次分配接口。
3. 不支持 memcg 记账
代码中有一条明确的限制检查:
/* Bulk allocator does not support memcg accounting. */ if (memcg_kmem_online() && (gfp & __GFP_ACCOUNT)) goto failed;
如果内存 cgroup 在线且分配请求带有__GFP_ACCOUNT,批量分配会直接失败,回退到单页分配路径。
4. 单页请求回退
如果nr_pages为 1,或者数组中只剩 1 个槽位需要填充,函数会回退到标准的单页分配接口,因为批量路径的开销不值得为单页付出。
分配策略与水位检查
批量分配的核心优化在于一次性判断 zone 是否有足够的内存满足整批需求,而不是为每一页单独做水位检查。水位检查的标记值计算方式为:
mark = wmark_pages(zone, alloc_flags & ALLOC_WMARK_MASK) + nr_pages - nr_populated;
这里nr_pages - nr_populated是本次调用还需要分配的页数上界,避免重复计算已存在的槽位。
在 PCP 锁下的批量分配循环
水位检查通过后,函数会获取 PCP 锁,然后在一个循环中连续从 PCP 链表取页,直到达到请求数量或 PCP 链表耗尽:
pcp_list = &pcp->lists[order_to_pindex(ac.migratetype, 0)]; while (nr_populated < nr_pages) { page = __rmqueue_pcplist(zone, 0, ac.migratetype, alloc_flags, pcp, pcp_list); if (unlikely(!page)) { /* 如果已经分配了至少一页,可以提前结束 */ if (!nr_populated) { pcp_spin_unlock(pcp); goto failed_irq; } break; } prep_new_page(page, 0, gfp, 0); set_page_refcounted(page); page_array[nr_populated++] = page; }这个循环的关键点是:只在开始时获取一次 PCP 锁,循环内部不再重复获取锁,大幅减少了锁操作开销。
缺失的追踪钩子(已修复)
在 2026 年 9 月的补丁中,开发者发现批量分配路径遗漏了trace_mm_page_alloc()和kmsan_alloc_page()调用,导致批量分配的页面在 ftrace/BPF/perf 中不可见,KMSAN 的影子内存也未更新。补丁在prep_new_page()和set_page_refcounted()之后添加了这两个钩子,与标准单页分配路径保持一致。
总结
alloc_pages_bulk_noprof()是内核为高性能批量单页分配设计的专用接口。它通过“一次锁、多次分配”的模式减少 PCP 锁竞争,通过“只填充 NULL 槽位”的语义支持增量填充,通过“整批水位检查”避免逐页判断的开销。它的约束也很明确:仅 order-0、不支持 memcg 记账、单页请求回退。