Linux内存管理(六): 伙伴系统与alloc_pages的分配策略

张开发
2026/4/16 23:46:31 15 分钟阅读

分享文章

Linux内存管理(六): 伙伴系统与alloc_pages的分配策略
1. 伙伴系统Linux物理内存管理的基石第一次看到伙伴系统这个词时我脑海中浮现的是两个形影不离的好朋友。实际上在Linux内核中伙伴系统Buddy System确实像一对默契的搭档只不过它们管理的是物理内存页面的分配与回收。这个从1963年就被提出的算法至今仍是Linux内核物理内存管理的核心机制。简单来说伙伴系统就像个智能仓库管理员把物理内存划分成不同大小的区块都是2的幂次方当应用程序申请内存时它会自动匹配最合适的区块大小。比如你需要5个页面它会给你8个2^3既避免了内存浪费又保证了分配效率。在内核源码mm/page_alloc.c中伙伴系统的实现堪称精妙。它通过11个MAX_ORDER默认值free_area链表来管理不同大小的内存块每个链表对应着2^0到2^(MAX_ORDER-1)个连续页面。我曾在调试内存泄漏时用crash工具查看过这些链表那种清晰的层级结构让人印象深刻。2. alloc_pages函数详解从API到底层实现alloc_pages()堪称Linux内核开发者的瑞士军刀这个定义在include/linux/gfp.h中的函数负责分配连续的物理内存页面。它的函数原型看起来很简单struct page *alloc_pages(gfp_t gfp_mask, unsigned int order);但就像Linux中许多看似简单的接口一样魔鬼藏在细节里。gfp_mask这个参数就包含了至少32种不同的标志位组合控制着内存分配的方方面面。记得我第一次使用时被__GFP_HIGHMEM、__GFP_ATOMIC这些标志搞得晕头转向直到在项目中出现几次内存分配失败后才真正理解它们的意义。这个函数最有趣的地方在于它的NUMA适配性。在非NUMA系统上它直接调用alloc_pages_node()在当前节点分配而在NUMA系统中它会根据内存策略如MPOL_INTERLEAVE进行跨节点分配。我在优化一个数据库应用时就曾通过调整NUMA策略获得了20%的性能提升。3. GFP掩码内存分配的行为指南GFPGet Free Page掩码就像是给内存分配器下的军令状它决定了从哪里分配DMA区域普通区域分配时能否休眠在中断上下文就不能休眠是否清空页面内容内存不足时如何处理内核中预定义了一些常用组合比如GFP_KERNEL最常用的标志可能引起休眠GFP_ATOMIC用于原子上下文不会休眠GFP_DMA从DMA区域分配我曾经踩过一个坑在中断处理函数中使用GFP_KERNEL导致内核崩溃。后来才明白中断上下文必须使用GFP_ATOMIC因为中断不能休眠。这个教训让我养成了仔细检查GFP标志的习惯。4. 分配策略快速路径与慢速路径的智慧alloc_pages的分配过程就像去餐厅吃饭快速路径get_page_from_freelist就像直接有空位立即入座慢速路径__alloc_pages_slowpath需要等位可能还要请人离开内存回收快速路径会直接检查伙伴系统的空闲链表如果找到合适大小的块就直接返回。而慢速路径则要复杂得多它可能会唤醒kswapd进行后台回收尝试内存压缩compaction直接回收内存reclaim最后甚至触发OOM killer我在分析一个性能问题时用ftrace捕捉到进程频繁进入慢速路径最终发现是因为内存碎片化严重。通过调整vm.extfrag_threshold参数情况得到了明显改善。5. 内存碎片化与ALLOC_NOFRAGMENT内存碎片化是物理内存管理的天敌。Linux通过多种机制应对迁移类型MIGRATE_TYPES将页面分为可移动、不可移动等类型ALLOC_NOFRAGMENT标志避免混合分配不同类型的页面块在mm/internal.h中定义的ALLOC_NOFRAGMENT标志特别有意思。当设置这个标志时分配器会尽量避免拆分大的连续内存块从而减少碎片。这就像停车场管理员会尽量把相邻车位留给大型车辆一样。我在优化一个视频处理应用时发现频繁的大块内存分配会导致系统碎片化严重。通过合理使用ALLOC_NOFRAGMENT标志不仅提高了分配成功率还降低了延迟。6. 实战经验调试内存分配问题在实际工作中我总结了几种调试alloc_pages问题的方法通过/proc/buddyinfo查看伙伴系统状态使用tracepoint跟踪分配过程echo 1 /sys/kernel/debug/tracing/events/kmem/mm_page_alloc/enable分析OOM killer日志使用vmstat观察内存压力指标曾经有个案例某台服务器偶尔会出现内存分配延迟飙升。通过分析发现当系统空闲内存低于low watermark时kswapd会被频繁唤醒。调整vm.min_free_kbytes后问题得到解决。7. 性能优化技巧根据我的经验优化内存分配性能有几个关键点合理设置gfp_mask能用GFP_NOWAIT就不用GFP_KERNEL预分配大块内存特别是对于需要连续物理内存的场景理解NUMA拓扑numactl工具很有用监控碎片化指标/proc/pagetypeinfo在实现一个高速网络包处理模块时我们通过预分配内存池的方式将内存分配耗时从微秒级降到了纳秒级。这再次证明了理解底层机制的重要性。

更多文章