Linux内核伙伴系统实战:alloc_pages快速分配与慢速分配全解析

发布时间:2026/7/15 0:46:27

Linux内核伙伴系统实战:alloc_pages快速分配与慢速分配全解析 Linux内核伙伴系统实战alloc_pages快速分配与慢速分配全解析在Linux内核开发中内存管理是最核心的子系统之一。作为内核开发者理解伙伴系统Buddy System的工作原理对于性能调优和问题排查至关重要。本文将深入剖析alloc_pages函数的两种分配路径快速分配get_page_from_freelist和慢速分配__alloc_pages_slowpath揭示内核如何高效管理物理内存。1. 伙伴系统基础架构Linux内核的伙伴系统负责管理物理内存页的分配与释放。它通过将内存划分为不同大小的块每个块是2的幂次方个页来实现高效的内存管理。这种设计既减少了内存碎片又保证了分配速度。伙伴系统的核心数据结构包括zone内存管理区如ZONE_DMA、ZONE_NORMAL等free_area管理不同阶order的空闲页块migratetype标识页块的迁移类型防止内存碎片struct zone { /* ... */ struct free_area free_area[MAX_ORDER]; /* ... */ }; struct free_area { struct list_head free_list[MIGRATE_TYPES]; unsigned long nr_free; };内存分配的基本单位是页通常4KB而alloc_pages函数则是从伙伴系统获取内存的主要接口。它的原型如下struct page *alloc_pages(gfp_t gfp_mask, unsigned int order);2. 快速分配路径get_page_from_freelist快速分配路径是内存分配的首选方案当系统内存充足时绝大多数分配请求都能通过此路径快速完成。其核心函数get_page_from_freelist的执行流程可分为四个关键阶段2.1 区域选择与权限检查内核首先遍历zonelist按优先级排序的内存区域列表检查每个zone是否满足CPU允许在该节点分配内存cpuset检查节点脏页未超过限制针对写操作当前zone的水位watermark满足要求水位检查是快速分配的关键决策点内核根据分配标志alloc_flags选择三种水位之一水位标志描述适用场景ALLOC_WMARK_MIN最低水位紧急分配ALLOC_WMARK_LOW低水位默认常规分配ALLOC_WMARK_HIGH高水位安全分配2.2 水位检查与快速决策当zone的空闲内存低于请求水位时内核有两种选择直接跳过该zone如果当前zone不允许回收或与首选zone距离过远尝试节点回收调用node_reclaim尝试回收部分内存if (!zone_watermark_fast(zone, order, mark, ac_classzone_idx(ac), alloc_flags)) { if (alloc_flags ALLOC_NO_WATERMARKS) goto try_this_zone; if (node_reclaim_mode 0 || !zone_allows_reclaim(ac-preferred_zoneref-zone, zone)) continue; ret node_reclaim(zone-zone_pgdat, gfp_mask, order); /* ... */ }2.3 实际内存分配通过检查后内核调用buffered_rmqueue从所选zone的空闲列表中分配内存当order0时从per-CPU缓存中分配单页当order0时直接从伙伴系统分配连续页块page buffered_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; }2.4 高阶原子分配处理对于高阶order0的原子分配GFP_ATOMIC内核会保留部分内存块在highatomic reserve中防止原子分配因内存不足而失败。这部分内存约占zone总内存的1%。3. 慢速分配路径__alloc_pages_slowpath当快速分配失败时内核进入慢速分配路径通过更激进的手段尝试获取内存。慢速路径的主要步骤包括3.1 初始准备阶段降低分配水位至ALLOC_WMARK_MIN唤醒kswapd内核线程开始异步内存回收再次尝试快速分配alloc_flags gfp_to_alloc_flags(gfp_mask); if (gfp_mask __GFP_KSWAPD_RECLAIM) wake_all_kswapds(order, ac); page get_page_from_freelist(gfp_mask, order, alloc_flags, ac);3.2 内存压缩尝试对于高阶order PAGE_ALLOC_COSTLY_ORDER分配内核尝试内存压缩compaction迁移可移动的页合并出更大的连续空闲块使用__alloc_pages_direct_compact触发直接压缩压缩优先级从DEF_COMPACT_PRIORITY12开始每次失败后增加优先级page __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac, INIT_COMPACT_PRIORITY, compact_result);3.3 直接内存回收当压缩仍不能满足需求时内核执行直接回收direct reclaim调用__alloc_pages_direct_reclaim同步回收内存回收后再次尝试分配page __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags, ac, did_some_progress);内存回收的效率直接影响后续分配的成功率。内核会记录是否有进展did_some_progress决定是否继续尝试。3.4 最终应急措施当所有常规手段都失败后内核采取最后措施OOM Killer选择并终止占用内存多的进程完全忽略水位设置ALLOC_NO_WATERMARKS标志重试整个流程检查cpuset是否变化后重新开始page __alloc_pages_may_oom(gfp_mask, order, ac, did_some_progress); if (!page (gfp_mask __GFP_NOFAIL)) page __alloc_pages_cpuset_fallback(gfp_mask, order, ALLOC_NO_WATERMARKS, ac);4. 实战优化技巧在实际内核开发中理解这些分配路径有助于优化内存使用。以下是几个关键优化点4.1 分配标志选择合理设置gfp_mask可以显著影响分配行为标志组合适用场景对分配路径的影响GFP_ATOMIC原子上下文仅尝试快速分配可能使用保留内存GFP_KERNEL常规内核分配允许进入慢速路径可能触发回收GFP_NOFS文件系统禁用限制文件系统相关操作GFP_NOIO块IO禁用限制块IO相关操作4.2 水位调整策略通过修改/proc/sys/vm/下的参数可以调整内存水位# 查看当前低水位设置 cat /proc/sys/vm/min_free_kbytes # 临时调整水线单位KB echo 8192 /proc/sys/vm/min_free_kbytes提示过高的水线会减少可用内存过低则可能增加分配延迟。4.3 监控与调试工具/proc/buddyinfo查看伙伴系统各阶空闲块cat /proc/buddyinfovmstat监控内存回收活动vmstat -stracepoints跟踪分配事件perf probe --add __alloc_pages_nodemask perf stat -e probe:__alloc_pages_nodemask -a sleep 104.4 性能调优案例场景一个网络服务在高负载下出现分配延迟分析步骤检查/proc/buddyinfo发现高阶连续内存不足使用ftrace确认慢速路径调用频繁分析/proc/vmstat发现压缩活动增加解决方案调整vm.min_free_kbytes增加保留内存优化应用内存使用模式减少高阶分配考虑启用透明大页THP减少碎片5. 进阶实现细节深入理解伙伴系统需要分析几个关键机制5.1 迁移类型与反碎片Linux通过迁移类型migratetype减少内存碎片enum migratetype { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_RECLAIMABLE, MIGRATE_PCPTYPES, /* 每CPU页的迁移类型 */ MIGRATE_HIGHATOMIC, /* 高阶原子分配 */ MIGRATE_CMA, /* 连续内存分配器 */ MIGRATE_ISOLATE, /* 不能分配 */ MIGRATE_TYPES };每种迁移类型维护独立的内存块链表防止不可移动页分散在内存中。5.2 每CPU页缓存单页order0分配通过每CPU缓存加速struct per_cpu_pages { int count; /* 列表中页数 */ int high; /* 高水线清空时触发填充 */ int batch; /* 添加/删除的块大小 */ struct list_head lists[MIGRATE_PCPTYPES]; };当缓存不足时内核从伙伴系统批量获取页batch参数控制数量。5.3 高阶原子保留机制为防止高阶原子分配失败内核维护highatomic reservestatic void reserve_highatomic_pageblock(struct page *page, struct zone *zone, unsigned int order) { /* 保留不超过zone的1/100页 */ zone-nr_reserved_highatomic (1 order); set_pageblock_migratetype(page, MIGRATE_HIGHATOMIC); }这部分内存在常规分配中不可用专门用于紧急的高阶原子分配。

相关新闻