Linux内核内存管理深度实战:从伙伴系统到Slab分配器
引言
Linux内核的内存管理是操作系统最复杂、最核心的子系统之一,负责管理物理内存的分配与回收、虚拟地址空间的映射、页面置换与回收。本文深入剖析伙伴系统、Slab分配器、虚拟内存管理、页表结构以及性能调优实战。
1. 内存管理架构全景
Linux内核采用分层架构:最底层是硬件相关层处理MMU和TLB访问;中间层是伙伴系统和Slab分配器管理物理页面分配;上层负责VMA管理、页缓存和映射。核心设计哲学是延迟分配(Demand Paging)——进程请求内存时并不立即分配物理页面,而是在真正访问时才通过缺页异常完成映射。
2. 伙伴系统(Buddy System)
伙伴系统管理连续物理内存,将页面按2的幂次分组(order 0~10,对应1页~1024页)。分配时从合适的order链表中取出一个块;若不存在则从更大的order拆分。释放时检查伙伴(buddy)是否也在空闲链表中,若是则合并。zone结构体中维护free_area数组,每个free_area包含MIGRATE_TYPE_NR个空闲链表,nr_free统计空闲页面数,使得分配和合并都能在O(1)时间完成。
核心函数alloc_pages(gfp_mask, order)的执行路径:从目标zone的free_area[order]获取空闲块;若失败则向更大order遍历,找到后逐级拆分(expand函数);触发直接回收(direct reclaim)或压缩(compaction);最终通过OOM Killer收尾。Linux引入MIGRATEABLE迁移类型以减少碎片:UNMOVABLE用于内核不可移动数据结构,MOVABLE用于用户态可移动页面,RECLAIMABLE用于可回收页缓存,CMA用于设备驱动大块连续内存分配。
3. Slab分配器
伙伴系统以页(通常4KB)为粒度分配,但内核经常需要小至几十字节的对象。Slab分配器在伙伴系统之上构建对象缓存池,完美解决内部碎片问题。Slab是Linus最初设计的经典实现;Slub是现代默认实现,简化设计提供更好调试支持;Slob是极简实现面向嵌入式系统。
Slub的核心是每CPU快速路径:kmem_cache_cpu的freelist直接提供对象,无锁设计保证多核扩展性。慢速路径从NUMA node的partial链表补充对象,若无可用slab则向伙伴系统申请新slab。Slab着色(Slab Coloring)通过color offset偏移避免多CPU缓存行冲突,对高并发场景性能有显著影响。
4. 虚拟内存与地址空间
在x86_64架构下,Linux进程的虚拟地址空间(48位有效)分为用户空间(128TB)和内核空间(128TB)。内核空间细分为直接映射区、vmalloc区、持久映射区和固定映射区。每个内存映射区域由vm_area_struct表示,形成红黑树 链表的混合结构,红黑树保证O(log n)区间查找,链表方便顺序遍历。
5. 多级页表与地址转换
x86_64使用4级页表:PML4到PDPT到PD到PT。每个表512项(9位索引),共4级36位索引 12位页内偏移=48位虚拟地址。TLB是页表项的硬件缓存,Linux使用PCID标记TLB项属于哪个进程,避免每次上下文切换都刷新整个TLB。Linux支持2MB和1GB大页减少页表层级和TLB压力,THP自动合并符合条件的普通页为大页。
6. 页面回收与交换
kswapd在每个NUMA节点后台运行,当zone空闲页面水位低于WMARK_LOW时被唤醒执行回收直到达到WMARK_HIGH。Linux维护active/inactive两组LRU链表(匿名页和文件页共四组),页面需要经历两次访问才会从inactive晋升到active。当空闲内存严重不足时,分配者线程会同步执行直接回收(Direct Reclaim),导致分配延迟剧烈抖动。
7. 实战调优与排查
OOM Killer基于oom_score选择进程终止,/proc/oom_score_adj允许调整优先级(-1000~1000)。关键调优参数:swappiness(0~200)控制匿名页vs文件页回收偏好(默认60);dirty_ratio限制脏页比例;overcommit_memory设置超分配策略;min_free_kbytes确保最小空闲内存。
内存碎片排查:通过/proc/buddyinfo和/proc/pagetypeinfo查看碎片分布,slabtop实时查看Slab使用,compact_memory触发内存压实。NUMA系统支持First Touch、Interleaved、MPOL_BIND等策略。
8. 总结
Linux内核内存管理是精密复杂的系统:伙伴系统保证物理页面高效分配与合并;Slab分配器以对象粒度满足小内存需求;多级页表结合TLB实现高效地址转换;LRU回收在内存压力下优雅取舍。理解这些机制是写出高效代码和排查性能问题的必要知识储备。

发表评论 取消回复