post_alloc_hook()是页面分配路径上的分配后钩子函数,负责在页面从伙伴系统取出并校验通过后,执行一系列分配后的初始化与标记操作,最终把页面交给调用方。
核心作用
它的职责是在页面可用之前完成所有必要的后处理,包括:KASAN 标签处理、内存清零、页面标志设置、alloc_tag 记录等。
参数解析
| 参数 | 含义 |
|---|---|
page | 分配到的页块首页 |
order | 分配阶数 |
gfp_flags | 分配标志(决定清零、标签等行为) |
核心流程
典型实现如下:
inline void post_alloc_hook(struct page *page, unsigned int order, gfp_t gfp_flags) { bool init = !want_init_on_free() && want_init_on_alloc(gfp_flags) && !should_skip_init(gfp_flags); bool init_tags = init && (gfp_flags & __GFP_ZEROTAGS); set_page_private(page, 0); set_page_refcounted(page); if (init_tags) { /* 清零并清除标签 */ for (i = 0; i != 1 << order; ++i) tag_clear_highpage(page + i); } else if (init) { /* 仅清零 */ kernel_init_pages(page, 1 << order); } /* KASAN 解毒(除非应跳过) */ if (!should_skip_kasan_unpoison(gfp_flags) && kasan_unpoison_pages(page, order, init)) { /* KASAN 已完成初始化 */ } else { /* 重置页面标签,确保 page_address() 安全 */ for (i = 0; i != 1 << order; ++i) page_kasan_tag_reset(page + i); } /* 设置迁移类型 */ set_pageblock_migratetype(page, MIGRATE_MOVABLE); // 视情况 /* alloc_tag 记录 */ pgalloc_tag_add(page, current, 1 << order); /* 架构相关后处理 */ arch_post_alloc_hook(page, order, gfp_flags); }逐步说明
1. 计算初始化需求
bool init = !want_init_on_free() && want_init_on_alloc(gfp_flags) && !should_skip_init(gfp_flags); bool init_tags = init && (gfp_flags & __GFP_ZEROTAGS);
want_init_on_alloc():init_on_alloc内核参数要求分配时清零;should_skip_init():前面讲的,HW_TAGS 下__GFP_SKIP_ZERO可跳过;init_tags:额外要求__GFP_ZEROTAGS,表示清零的同时清除 KASAN 标签。
2. 清除私有字段与设置引用计数
set_page_private(page, 0); set_page_refcounted(page);
set_page_private(page, 0):清除private字段(可能残留 Buddy 信息);set_page_refcounted(page):把引用计数设为 1(页已分配出去)。
3. 清零与标签处理
init_tags为真:用tag_clear_highpage()清零并清标签;init为真:用kernel_init_pages()仅清零;二者都不做则跳过。
4. KASAN 解毒
if (!should_skip_kasan_unpoison(gfp_flags) && kasan_unpoison_pages(page, order, init)) { /* KASAN 完成初始化 */ } else { for (i = 0; i != 1 << order; ++i) page_kasan_tag_reset(page + i); }should_skip_kasan_unpoison():前面讲的,决定是否跳过解毒;kasan_unpoison_pages():标记内存可访问;若跳过或 KASAN 不处理,则重置页面标签,保证
page_address()安全。
5. alloc_tag 记录
pgalloc_tag_add(page, current, 1 << order);
记录分配标签,更新统计。
6. 架构相关钩子
arch_post_alloc_hook(page, order, gfp_flags);
架构相关后处理(如某些架构需要特殊标记)。
在分配路径中的位置
get_page_from_freelist() └── prep_new_page(page, order, gfp_mask, alloc_flags) ├── check_new_pages(page, order) // 校验 ├── post_alloc_hook(page, order, gfp_mask) // ← 这里 │ ├── 清零/标签处理 │ ├── KASAN 解毒 │ ├── alloc_tag 记录 │ └── arch 钩子 └── ...
总结
post_alloc_hook()是页面分配路径的后处理中枢:它根据init_on_alloc、__GFP_ZERO、__GFP_ZEROTAGS等标志决定是否清零、是否清标签,处理 KASAN 解毒与标签重置,记录 alloc_tag 分配信息,并调用架构钩子。它确保了从伙伴系统取出的页在交给调用方之前,处于干净、安全、可追踪的状态。