prepare_alloc_pages()是物理页分配路径的准备与上下文初始化函数。它在__alloc_frozen_pages_noprof()进入快速路径之前被调用负责把原始参数gfp_mask、order、节点掩码等整理成struct alloc_context并计算初始的alloc_gfp和alloc_flags。核心作用它的职责可以概括为决定“从哪分配、允许什么、初始水位如何”。具体包括构造分配上下文aczonelist、highest_zoneidx、nodemask、preferred_zoneref、migratetype 等根据current_gfp_context()调整实际的gfp_mask将 GFP 标志转换为ALLOC_*标志处理__GFP_NOFRAGMENT防碎片标志。核心流程static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order, int preferred_nid, nodemask_t *nodemask, struct alloc_context *ac, gfp_t *alloc_gfp, unsigned int *alloc_flags) { /* 1. 根据当前任务上下文调整 gfp_mask */ *alloc_gfp gfp_mask; if (gfp_mask __GFP_NOWARN) *alloc_gfp ~__GFP_NOWARN; // 由调用方处理不进主 mask /* 2. 设置 migratetype根据 __GFP_MOVABLE 等 */ ac-migratetype gfp_migratetype(gfp_mask); /* 3. 检查 cpuset/mempolicy 并初始化 nodemask、highest_zoneidx */ if (cpusets_enabled()) { *alloc_gfp | __GFP_HARDWALL; // 默认遵守 cpuset 硬墙 if (in_interrupt()) *alloc_gfp | __GFP_HIGH; if (!ac-nodemask) ac-nodemask cpuset_current_mems_allowed; } /* 4. 应用 mempolicy 的 nodemask */ ac-nodemask nodemask; ac-highest_zoneidx gfp_zone(gfp_mask); // 允许的最高 zone 类型 /* 5. 初始化 zonelist 和 preferred_zoneref */ ac-zonelist node_zonelist(preferred_nid, gfp_mask); ac-preferred_zoneref first_zones_zonelist(ac-zonelist, ac-highest_zoneidx, ac-nodemask); if (!ac-preferred_zoneref-zone) goto nopage; // 找不到合适的 zone直接失败 /* 6. 计算初始 alloc_flags */ *alloc_flags gfp_to_alloc_flags(gfp_mask, order); /* 7. 处理 __GFP_NOFRAGMENT */ if (gfp_mask __GFP_NOFRAGMENT) *alloc_flags | ALLOC_NOFRAGMENT; return true; nopage: return false; }逐步说明1.*alloc_gfp gfp_mask把原始gfp_mask存入alloc_gfp供后续路径使用。__GFP_NOWARN被单独处理因为它只影响告警行为不影响分配决策。2.ac-migratetype gfp_migratetype(gfp_mask)gfp_migratetype()根据__GFP_MOVABLE、__GFP_RECLAIMABLE等标志决定本次分配的迁移类型__GFP_MOVABLE→MIGRATE_MOVABLE__GFP_RECLAIMABLE→MIGRATE_RECLAIMABLE否则 →MIGRATE_UNMOVABLE。3. cpuset 与 mempolicy 处理启用 cpuset 时默认加__GFP_HARDWALL强制遵守 cpuset 的内存节点限制。中断上下文中额外加__GFP_HIGH因为中断分配不能等待。如果没有指定nodemask使用cpuset_current_mems_allowed。4.ac-highest_zoneidx gfp_zone(gfp_mask)gfp_zone()根据gfp_mask决定本次分配允许的最高 zone 类型GFP_DMA→ZONE_DMAGFP_DMA32→ZONE_DMA32GFP_HIGHUSER→ZONE_HIGHMEM普通GFP_KERNEL→ZONE_NORMAL。5. 初始化 zonelist 与preferred_zonerefac-zonelist node_zonelist(preferred_nid, gfp_mask)获取首选节点的 zonelist。first_zones_zonelist()在 zonelist 中找第一个满足highest_zoneidx和nodemask的 zone作为preferred_zoneref。如果找不到preferred_zoneref-zone为空直接goto nopage返回false分配失败。6.*alloc_flags gfp_to_alloc_flags(gfp_mask, order)调用前面讲过的gfp_to_alloc_flags()把 GFP 标志转成ALLOC_*标志水位、cpuset、原子特权等。7.__GFP_NOFRAGMENT如果调用方要求防碎片加上ALLOC_NOFRAGMENT指示伙伴系统尽量避免跨迁移类型偷页。 在分配路径中的位置text复制下载__alloc_frozen_pages_noprof(gfp, order, preferred_nid, nodemask) ├── struct alloc_context ac {}; ├── gfp_t alloc_gfp; ├── unsigned int alloc_flags; ├── if (!prepare_alloc_pages(gfp, order, preferred_nid, nodemask, │ ac, alloc_gfp, alloc_flags)) │ return NULL; // 准备失败 ├── alloc_gfp | __GFP_NOWARN; // 快路径重试时抑制告警 ├── page get_page_from_freelist(alloc_gfp, order, alloc_flags, ac); └── if (!page) page __alloc_pages_slowpath(alloc_gfp, order, ac);⚠️ 关键设计点1.preferred_zoneref的作用它标记了 zonelist 中第一个可用的 zone是快速路径和慢速路径遍历的起点。如果它为 NULL说明在当前 cpuset/mempolicy/highest_zoneidx 约束下没有任何 zone 可用直接失败无需进入慢速路径。2.__GFP_HARDWALL与 cpuset__GFP_HARDWALL表示严格遵守 cpuset 限制。用户态分配默认带此标志内核内部某些分配可能不带允许突破 cpuset。3.gfp_migratetype()与反碎片迁移类型的选择直接影响伙伴系统的分配策略和碎片化。__GFP_MOVABLE用于用户态可移动页使其能进入MIGRATE_MOVABLE链表便于后续规整迁移。 总结prepare_alloc_pages()是物理页分配的准备与初始化中枢它根据gfp_mask确定迁移类型、允许的最高 zone 类型应用 cpuset/mempolicy 约束构造nodemask初始化 zonelist 和preferred_zoneref并通过gfp_to_alloc_flags()计算出初始的ALLOC_*标志。如果找不到任何可用的 zone直接返回false让分配失败。它是__alloc_frozen_pages_noprof()从原始参数到可执行分配上下文的关键转换步骤。