在Linux内核中,页面分配器通过 gfp_mask(分配掩码) 确定可以从哪些内存区域(zone)分配内存。这一过程涉及对zone的优先级排序和条件检查,具体逻辑如下:
一、内存区域(zone)的分类
Linux内核将物理内存划分为多个zone,常见的有:
ZONE_DMA
供DMA设备使用的物理内存(通常为前16MB)。
ZONE_DMA32
供32位DMA设备使用的内存(通常为前4GB)。
ZONE_NORMAL
内核可直接映射的普通内存(64位系统中通常覆盖全部物理内存)。
ZONE_HIGHMEM
仅在32位系统中存在,用于管理超过内核直接映射范围的内存(如物理内存 > 896MB)。
二、从gfp_mask解析zone优先级
分配器通过gfp_mask中的标志位确定允许的zone列表及其优先级顺序:
1. 直接指定目标zone
__GFP_DMA
强制从ZONE_DMA分配。
__GFP_DMA32
强制从ZONE_DMA32分配。
__GFP_HIGHMEM
允许从ZONE_HIGHMEM分配(仅在32位系统有效)。
2. 隐式zone选择(默认逻辑)
若未明确指定zone标志,分配器按以下优先级选择zone:
ZONE_NORMAL → ZONE_DMA32 → ZONE_DMA
适用于大多数内核分配请求(如GFP_KERNEL)。
ZONE_HIGHMEM → ZONE_NORMAL → ...
仅当用户请求包含__GFP_HIGHMEM且系统为32位时触发。
三、分配流程
1.解析gfp_mask
根据标志位生成允许的zone列表(例如__GFP_DMA | __GFP_NORMAL表示允许ZONE_DMA和ZONE_NORMAL)。
2.按优先级遍历zone
从最高优先级(如ZONE_DMA)到最低优先级(如ZONE_HIGHMEM)依次检查每个zone。
3.检查zone的水位线(watermark)
仅当zone的空闲页数 ≥ 最低水位线(min) 时,才允许分配。
若gfp_mask包含__GFP_HIGH或__GFP_ATOMIC,允许使用紧急保留内存(低于min但高于min+reserve)。
4.选择第一个满足条件的zone
一旦找到符合条件的zone,立即从中分配内存。
四、示例场景
场景1:DMA内存分配
gfp_mask = __GFP_DMA | GFP_KERNEL;
允许的zone:仅ZONE_DMA。
检查逻辑:若ZONE_DMA的空闲页 ≥ min,则分配;否则失败。
场景2:默认内核分配
gfp_mask = GFP_KERNEL; // 不指定zone标志
允许的zone:ZONE_NORMAL → ZONE_DMA32 → ZONE_DMA(按优先级)。
检查逻辑:依次检查每个zone的水位线,选择第一个满足条件的zone。
场景3:GFP_ATOMIC
分配过程不可睡眠,优先从预留内存或ZONE_NORMAL快速分配,避免触发复杂回收机制。
场景4:GFP_HIGHUSER_MOVABLE
包含__GFP_HIGHMEM标志,优先从ZONE_HIGHMEM分配用户空间可移动的页面。
五、核心函数与代码路径
1.get_page_from_freelist()
遍历zone列表,检查水位线并尝试分配。
2.gfp_zone(gfp_mask)
将gfp_mask转换为zone类型(如ZONE_DMA)。
3.zone_watermark_ok()
检查指定zone的水位线是否满足分配条件。
六、性能优化与注意事项
1.避免过度分散分配
频繁指定__GFP_DMA可能导致ZONE_NORMAL碎片化。
2.NUMA架构扩展
在多节点(NUMA)系统中,gfp_mask可能包含__GFP_THISNODE,强制从本地节点分配。
3.调试支持
启用CONFIG_DEBUG_VM时,分配器会严格检查gfp_mask与zone的合法性。
七、实现代码逻辑
在源码中,zone选择的核心逻辑如下(以Linux 5.x为例):
// include/linux/gfp.h
#define gfp_zone(mask) ((mask) & (__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32))
// mm/page_alloc.c
struct page *alloc_pages(gfp_t gfp_mask, unsigned int order) {
enum zone_type highest_zoneidx = gfp_zone(gfp_mask);
// 遍历允许的zone,尝试分配内存
for_each_zone_zoneidx(zone, highest_zoneidx) {
page = __alloc_pages_cpuset_fallback(gfp_mask, order, zone);
if (page) return page;
}
return NULL;
}
总结:gfp_mask通过标志位隐式或显式定义允许的zone范围,分配器按优先级和内存水位线选择第一个可用的zone。这一机制在保证硬件兼容性(如DMA)的同时,优化了内存分配效率。