3. 物理内存管理:伙伴系统算法、slab分配器、内存碎片化问题
物理内存管理,说白了就是操作系统怎么把有限的物理内存条,高效地分配给各个进程使用。这可不是简单的“谁要就给谁”。内核里有两套核心机制在协同工作:伙伴系统和slab分配器。一个管大块内存的分配与回收,另一个管内核自身频繁创建的小对象。搞懂它们,你才算真正入了内存管理的门。
3.1 伙伴系统算法:物理内存的“大管家”
伙伴系统负责管理物理内存的“大块”分配。它把物理内存按照2的幂次方(4KB、8KB、16KB……一直到4MB甚至更大)划分成一个个“块”。每个块都有一个“伙伴”——也就是大小相同、地址相邻的另一个块。
分配逻辑很简单: 你要一个2^3=8KB的块,系统就从8KB的空闲链表里找。如果没有,就从16KB的链表里拆一个出来,一半给你,另一半(它的伙伴)挂回8KB的链表。如果16KB也没有,就继续向上拆,直到找到合适的。
释放逻辑是逆过程: 你释放一个8KB的块,系统会检查它的“伙伴”是否也是空闲的。如果是,就把两个8KB合并成一个16KB的块,挂回16KB的链表。然后继续检查这个16KB的伙伴……直到无法合并为止。
我早年在一台运行了半年的数据库服务器上,发现内存分配越来越慢。用/proc/buddyinfo一看,好家伙,2^0(4KB)的块几乎被耗尽,而2^4(64KB)以上的大块却堆积如山。这就是典型的“外碎片化”——大块内存被拆成了无数小块,但无法合并回去。
/proc/buddyinfo显示大量不可移动的页面阻塞了内存合并。记住,/proc/buddyinfo是诊断物理内存碎片的第一现场。
3.2 slab分配器:内核对象的“专属管家”
伙伴系统分配的最小单位是4KB(一页)。但内核里大量需要的是几十字节、几百字节的小对象,比如进程描述符task_struct、文件对象file、网络缓冲区sk_buff。如果每次都找伙伴系统要一页,那浪费就太大了。
slab分配器就是干这个的。它从伙伴系统申请一整页或多页内存,然后把它切分成多个大小相同的“对象缓存”。每个缓存专门管理一种内核对象。
slab分配器的核心优势:
- 对象复用: 对象释放后,不会真的归还给伙伴系统,而是留在slab缓存里,下次分配直接取用,避免了频繁的页分配和释放。
- 硬件缓存友好: 同类型的对象在物理上连续存放,能提高CPU缓存的命中率。
- 着色(Coloring): 通过偏移对象在slab中的起始位置,让不同slab中的对象错开,减少CPU缓存行冲突。
你可以通过slabtop命令实时查看slab缓存的使用情况。我个人习惯在系统上线前,先跑一轮压力测试,然后用slabtop -s c按缓存大小排序,看看哪些内核对象的缓存增长异常。
3.3 内存碎片化:两大“顽疾”
内存碎片化分为两种:外部碎片和内部碎片。伙伴系统主要面临外部碎片,slab分配器主要面临内部碎片。
| 碎片类型 | 产生机制 | 影响范围 | 典型场景 |
|---|---|---|---|
| 外部碎片 | 频繁分配和释放不同大小的块,导致空闲内存被分割成不连续的小块 | 伙伴系统 | 长时间运行的服务器,大块内存分配失败 |
| 内部碎片 | 分配的内存块大于实际请求的大小 | slab分配器、伙伴系统 | slab缓存对齐、伙伴系统按2的幂次分配 |
你想想看,外部碎片最头疼的地方在哪?就是明明总空闲内存还有很多,但就是分配不出一个连续的、足够大的块。比如你要分配一个2MB的连续DMA缓冲区,但物理内存被拆成了无数个4KB的小碎片,那就只能干瞪眼。
内核的应对策略:
- 内存规整(Compaction): 内核会尝试把可移动的页面迁移到一起,腾出连续的大块空间。你可以通过
/proc/sys/vm/compact_memory手动触发。 - 可移动页面(Movable Pages): 内核将页面分为“可移动”、“可回收”、“不可移动”等类型。规整时,只移动“可移动”的页面,避免影响内核核心数据结构。
- 虚拟可移动内存(ZONE_MOVABLE): 专门划出一块区域,里面的页面都是可移动的,用于用户态进程的匿名页和页缓存。
我曾经遇到一个案例:一台Nginx服务器运行了两个月后,突然无法分配新的网络缓冲区,导致大量连接超时。排查后发现,/proc/buddyinfo中所有order大于3的块都是0,而/proc/pagetypeinfo显示“不可移动”的页面占据了大量内存。最终通过echo 1 > /proc/sys/vm/compact_memory触发内存规整,才恢复了正常。
cat /proc/buddyinfo —— 查看各order的空闲块数量。cat /proc/pagetypeinfo —— 查看各迁移类型的页面分布。cat /proc/slabinfo —— 查看slab缓存的使用详情。
3.4 知识结构图:物理内存管理全景
下面这张图,把伙伴系统、slab分配器和内存碎片化的关系串起来了。你可以把它当作本章的“思维导图”。
嗯,这张图把整个脉络理清楚了。伙伴系统是地基,slab分配器是上层建筑,碎片化是两者共同面临的挑战。理解了这个层次关系,你再去分析内存问题,思路就会清晰很多。
/proc/buddyinfo、/proc/pagetypeinfo和slabtop。