☰
page_alloc gfp_to_alloc_flags
2026/10/13 6:56:38 网站建设 项目流程

gfp_to_alloc_flags()是分配路径上的标志转换函数,负责把用户/调用方传入的gfp_t(GFP 标志)转换成分配器内部使用的ALLOC_*标志。它决定了本次分配能使用哪些水位、能否访问保留区、是否遵守 cpuset 限制等。

核心作用与定位

gfp_t是对外接口(如GFP_KERNEL、GFP_ATOMIC),表达调用方的语义需求;ALLOC_*是对内标志,控制分配器的具体行为。gfp_to_alloc_flags()是两者之间的桥梁,由__alloc_frozen_pages_noprof()在进入分配路径前调用。

参数解析

参数含义
gfp_maskGFP 标志(注意:需已经过current_gfp_context()处理)
order分配阶数(影响某些标志的启用)

返回值:unsigned int,转换后的ALLOC_*标志组合。

核心逻辑

static inline unsigned int gfp_to_alloc_flags(gfp_t gfp_mask, unsigned int order) { unsigned int alloc_flags = ALLOC_WMARK_MIN | ALLOC_CPUSET; /* * 原子分配(不能睡眠)需要使用更低的水位线, * 并且可以在必要时访问内存保留区。 */ if (unlikely(gfp_mask & (__GFP_ATOMIC | __GFP_DIRECT_RECLAIM)) == __GFP_ATOMIC) alloc_flags |= ALLOC_HARDER; /* * 高优先级分配(如 PF_MEMALLOC)可以访问所有保留区。 */ if (gfp_mask & __GFP_NOMEMALLOC) ; /* 明确拒绝访问保留区 */ else if (gfp_mask & __GFP_HIGH) alloc_flags |= ALLOC_HARDER; else if (current->flags & PF_MEMALLOC) alloc_flags |= ALLOC_NO_WATERMARKS; /* * 处理 OOM 上下文。 */ if (current->flags & PF_MEMALLOC) alloc_flags |= ALLOC_NO_WATERMARKS; /* * 检查是否可以使用 CMA。 */ alloc_flags = gfp_to_alloc_flags_cma(gfp_mask, alloc_flags); return alloc_flags; }

逐步说明

1. 基础标志

unsigned int alloc_flags = ALLOC_WMARK_MIN | ALLOC_CPUSET;
  • ALLOC_WMARK_MIN:默认使用最低水位线WMARK_MIN。这是普通分配的要求——空闲页必须高于WMARK_MIN。

  • ALLOC_CPUSET:默认遵守 cpuset 的内存节点限制。

2. 原子分配 →ALLOC_HARDER

if (unlikely(gfp_mask & (__GFP_ATOMIC | __GFP_DIRECT_RECLAIM)) == __GFP_ATOMIC) alloc_flags |= ALLOC_HARDER;
  • 条件:带__GFP_ATOMIC且不带__GFP_DIRECT_RECLAIM(即“纯原子分配”,不能睡眠、不能直接回收)。

  • 原子分配失败后果严重(如中断上下文),所以赋予ALLOC_HARDER特权:

    • 使用更低的水位线(WMARK_MIN / 4之类);

    • 可以尝试MIGRATE_HIGHATOMIC保留区。

3. 高优先级 →ALLOC_HARDER或ALLOC_NO_WATERMARKS

if (gfp_mask & __GFP_NOMEMALLOC) ; /* 明确拒绝访问保留区 */ else if (gfp_mask & __GFP_HIGH) alloc_flags |= ALLOC_HARDER; else if (current->flags & PF_MEMALLOC) alloc_flags |= ALLOC_NO_WATERMARKS;
  • __GFP_NOMEMALLOC:调用方明确拒绝使用内存保留区,不做任何特权提升。

  • __GFP_HIGH:高优先级分配(如GFP_ATOMIC通常隐含),加ALLOC_HARDER,可以使用部分保留。

  • PF_MEMALLOC:当前任务本身是内存回收者(如 kswapd、直接回收中的任务),赋予ALLOC_NO_WATERMARKS——完全无视水位线,可访问所有保留区。这是为了防止回收者自己被“饿死”导致死锁。

4.PF_MEMALLOC的重复检查

if (current->flags & PF_MEMALLOC) alloc_flags |= ALLOC_NO_WATERMARKS;
  • 某些版本在此处再次检查PF_MEMALLOC,确保回收上下文一定获得ALLOC_NO_WATERMARKS。

5. CMA 处理

alloc_flags = gfp_to_alloc_flags_cma(gfp_mask, alloc_flags);
  • gfp_to_alloc_flags_cma(),若分配是__GFP_MOVABLE,加上ALLOC_CMA允许使用 CMA 区域。

与__alloc_pages_slowpath()的配合

慢速路径中会进一步调整alloc_flags:

/* 慢速路径中,逐步放宽限制 */ if (!did_some_progress) { if (gfp_mask & __GFP_DIRECT_RECLAIM) alloc_flags |= ALLOC_HARDER; // 回收无进展,提升优先级 if (gfp_mask & __GFP_NOFAIL) alloc_flags |= ALLOC_NO_WATERMARKS; // NOFAIL 可无视水位 }

即:gfp_to_alloc_flags()给出初始标志,慢速路径根据回收进展动态调整。

历史演进

ALLOC_CPUSET的拆分:早期gfp_to_alloc_flags()还负责处理 cpuset 相关逻辑,后来拆分为gfp_to_alloc_flags()(标志转换)和__alloc_pages_cpuset_fallback()(cpuset 降级)。

order参数的加入:某些版本中gfp_to_alloc_flags()带order参数,用于ALLOC_HARDER的判断(高阶原子分配更严格)。

gfp_to_alloc_flags_cma()的拆分:CMA 逻辑独立成函数(前面讲过),使主函数更清晰,也满足“必须在current_gfp_context()之后调用”的约束。

总结

gfp_to_alloc_flags()是分配路径的标志翻译器:它把对外的gfp_t转换为对内的ALLOC_*标志——默认使用WMARK_MIN水位和遵守 cpuset;原子分配获得ALLOC_HARDER特权;__GFP_HIGH提升到ALLOC_HARDER;PF_MEMALLOC任务获得ALLOC_NO_WATERMARKS(完全无视水位,防死锁);__GFP_NOMEMALLOC明确拒绝特权;最后通过gfp_to_alloc_flags_cma()处理 CMA。它是__alloc_frozen_pages_noprof()进入分配路径前的基础转换步骤。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询