prep_new_page()是页面分配路径上的新页准备函数,负责在页面从伙伴系统取出后、交给调用方之前,完成校验和后处理,确保页面处于干净、可用的状态。
核心作用
它是get_page_from_freelist()中,页面分配成功后的最后一道准备工序。名字中的 "prep" 即 "prepare",表示"把新页准备好"。
参数解析
| 参数 | 含义 |
|---|---|
page | 分配到的页块首页 |
order | 分配阶数 |
gfp_flags | 分配标志 |
alloc_flags | 分配器内部标志(决定水位、回收策略等) |
核心流程
典型实现如下:
static void prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags, unsigned int alloc_flags) { /* * 1. 校验:确保页处于预期状态 */ if (unlikely(!check_new_pages(page, order))) { /* 坏页,已被 check_new_pages 报告并处理 */ return; } /* * 2. 分配后处理:清零、KASAN、alloc_tag、arch 钩子 */ post_alloc_hook(page, order, gfp_flags); /* * 3. 设置迁移类型(若需要) */ if (!free_pages_prepared(page, order)) { /* 某些情况下可能需要调整 */ ... } /* * 4. 若请求 __GFP_ZERO,清零 * (注意:可能已在 post_alloc_hook 里处理) */ ... }(具体实现随内核版本,但核心是校验 + 后处理两步。)
逐步说明
1. 页状态校验
if (unlikely(!check_new_pages(page, order))) return;
check_new_pages():遍历页块每一页,用check_new_page()校验。检查项:引用计数为 0、映射计数合法、无异常标志。
异常则
check_new_page_bad()报告坏页,prep_new_page()直接返回,不交给调用方。调试配置下生效,生产内核可能跳过。
2. 分配后钩子
post_alloc_hook(page, order, gfp_flags);
post_alloc_hook()完成:计算
init/init_tags,决定是否清零、清标签;调用
kernel_init_pages()或tag_clear_highpage();KASAN 解毒 / 标签重置;
pgalloc_tag_add()记录分配标签;arch_post_alloc_hook()架构钩子。
3. 迁移类型与页面标志
某些版本会在此设置页面的迁移类型、清除
PageBuddy等残留标志。确保页面作为"已分配页"的状态正确。
在分配路径中的位置
get_page_from_freelist() ├── 从伙伴系统取页(rmqueue 系列) └── prep_new_page(page, order, gfp_mask, alloc_flags) // ← 这里 ├── check_new_pages(page, order) // 校验 │ └── check_new_page(page + i) // 逐页 └── post_alloc_hook(page, order, gfp_flags) // 后处理 ├── 清零/标签处理 ├── KASAN 解毒 ├── alloc_tag 记录 └── arch 钩子
关键设计点
1. 校验与后处理分离
check_new_pages()只做校验,post_alloc_hook()只做处理。职责清晰,便于各自优化和调试。
2. 坏页阻断
校验失败直接返回,不执行后处理,避免坏页被交给调用方。
这是内存管理安全的最后一道防线。
3. 调试配置控制
校验部分在
CONFIG_DEBUG_VM下才生效;后处理部分(清零、KASAN 等)根据实际配置和标志决定。
总结
prep_new_page()是页面分配路径的准备中枢:它先通过check_new_pages()校验页块每一页的状态,异常则报告并阻断;正常则调用post_alloc_hook()完成清零、KASAN 处理、alloc_tag 记录等后处理,最终把干净、可用的页交给调用方。它是页面从"伙伴系统空闲页"转变为"可用分配页"的关键一步。