page_alloc get_page_from_freelist

📅 发布时间:2026/10/13 16:40:10
page_alloc get_page_from_freelist
get_page_from_freelist()是伙伴系统快速路径的核心分配函数负责遍历 zonelist找到第一个满足水位条件的 zone 并尝试分配。核心作用与定位它是__alloc_frozen_pages_noprof()旧版__alloc_pages_nodemask()在首次尝试分配时调用的函数。如果快速路径失败才会进入__alloc_pages_slowpath()慢速路径。核心逻辑static struct page * get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags, const struct alloc_context *ac) { struct zoneref *z ac-preferred_zoneref; struct zone *zone; struct pglist_data *last_pgdat_dirty_limit NULL; bool no_fallback; retry: no_fallback alloc_flags ALLOC_NOFRAGMENT; z ac-preferred_zoneref; for_next_zone_zonelist_nodemask(zone, z, ac-zonelist, ac-high_zoneidx, ac-nodemask) { struct page *page; unsigned long mark; /* cpuset 限制检查 */ if (cpusets_enabled() (alloc_flags ALLOC_CPUSET) !__cpuset_zone_allowed(zone, gfp_mask)) continue; /* 脏页限制检查文件页缓存写分配 */ if (ac-spread_dirty_pages) { ... } /* ALLOC_NOFRAGMENT NUMA 远程节点丢弃 NOFRAGMENT 重试 */ if (no_fallback nr_online_nodes 1 zone ! ac-preferred_zoneref-zone) { int local_nid zone_to_nid(ac-preferred_zoneref-zone); if (zone_to_nid(zone) ! local_nid) { alloc_flags ~ALLOC_NOFRAGMENT; goto retry; } } /* 水位检查 */ mark wmark_pages(zone, alloc_flags ALLOC_WMARK_MASK); if (!zone_watermark_fast(zone, order, mark, ac_classzone_idx(ac), alloc_flags)) { /* deferred pages 补充 */ if (static_branch_unlikely(deferred_pages)) { if (_deferred_grow_zone(zone, order)) goto try_this_zone; } /* ALLOC_NO_WATERMARKS跳过水位限制 */ if (alloc_flags ALLOC_NO_WATERMARKS) goto try_this_zone; /* node reclaim 判断 */ if (node_reclaim_mode 0 || !zone_allows_reclaim(ac-preferred_zoneref-zone, zone)) continue; ret node_reclaim(zone-zone_pgdat, gfp_mask, order); switch (ret) { case NODE_RECLAIM_NOSCAN: continue; case NODE_RECLAIM_FULL: continue; default: if (zone_watermark_ok(zone, order, mark, ac_classzone_idx(ac), alloc_flags)) goto try_this_zone; continue; } } try_this_zone: /* 实际分配 */ page rmqueue(ac-preferred_zoneref-zone, zone, order, gfp_mask, alloc_flags, ac-migratetype); if (page) { prep_new_page(page, order, gfp_mask, alloc_flags); /* 高阶原子分配保留 highatomic pageblock */ if (unlikely(order (alloc_flags ALLOC_HARDER))) reserve_highatomic_pageblock(page, zone, order); return page; } } return NULL; }逐步说明1. 遍历 zonelistfor_next_zone_zonelist_nodemask(zone, z, ac-zonelist, ac-high_zoneidx, ac-nodemask)从preferred_zoneref开始遍历所有符合条件的 zonezone 类型 ≤ 首选类型且节点在 nodemask 中。2. cpuset 检查if (cpusets_enabled() (alloc_flags ALLOC_CPUSET) !__cpuset_zone_allowed(zone, gfp_mask)) continue;如果启用了 cpuset 且调用方要求检查跳过不允许的 zone。3. 脏页限制检查if (ac-spread_dirty_pages) { if (last_pgdat_dirty_limit zone-zone_pgdat) continue; if (!node_dirty_ok(zone-zone_pgdat)) { last_pgdat_dirty_limit zone-zone_pgdat; continue; } }当调用方设置__GFP_WRITE文件页缓存写分配时检查节点的脏页数是否超限避免单节点脏页过多。4. ALLOC_NOFRAGMENT 与 NUMA 远程节点if (no_fallback nr_online_nodes 1 zone ! ac-preferred_zoneref-zone) { int local_nid zone_to_nid(ac-preferred_zoneref-zone); if (zone_to_nid(zone) ! local_nid) { alloc_flags ~ALLOC_NOFRAGMENT; goto retry; } }如果启用了ALLOC_NOFRAGMENT避免碎片但当前 zone 是远程节点则丢弃 NOFRAGMENT 标志并重新扫描。因为跨节点分配时防碎片的意义不大。5. 水位检查mark wmark_pages(zone, alloc_flags ALLOC_WMARK_MASK); if (!zone_watermark_fast(zone, order, mark, ac_classzone_idx(ac), alloc_flags))zone_watermark_fast()检查水位。6. 水位不足时的处理deferred pages 补充启动早期内存未完全初始化时尝试补充后重试。ALLOC_NO_WATERMARKS高优先级分配如 PF_MEMALLOC跳过水位检查直接分配。node reclaim如果开启了节点回收且回收距离合法尝试回收该节点的内存成功后重新检查水位。7. 实际分配try_this_zone: page rmqueue(ac-preferred_zoneref-zone, zone, order, gfp_mask, alloc_flags, ac-migratetype); if (page) { prep_new_page(page, order, gfp_mask, alloc_flags); if (unlikely(order (alloc_flags ALLOC_HARDER))) reserve_highatomic_pageblock(page, zone, order); return page; }水位满足后调用rmqueue()从 PCP 或伙伴系统取页再通过prep_new_page()校验和初始化。高阶原子分配还会保留 highatomic pageblock。在分配路径中的位置__alloc_frozen_pages_noprof() ├── get_page_from_freelist(...) // 快速路径 │ ├── 遍历 zonelist │ ├── zone_watermark_fast() // 水位检查 │ ├── node_reclaim() // 条件性回收 │ └── rmqueue() prep_new_page() // 取页并准备 └── if (!page) __alloc_pages_slowpath(...) // 慢速路径总结get_page_from_freelist()是伙伴系统快速路径的核心调度器它遍历 zonelist对每个 zone 执行 cpuset/脏页限制检查、ALLOC_NOFRAGMENT远程节点处理、zone_watermark_fast()水位判断水位不足时尝试 deferred pages 补充或 node reclaim满足条件后调用rmqueue()取页并通过prep_new_page()完成初始化。它是__alloc_frozen_pages_noprof()首次分配尝试的执行者失败后才进入慢速路径。