一、引言
Linux 内核的内存管理是操作系统中最复杂、最核心的子系统之一。它不仅负责物理内存的分配与回收,还要在性能、碎片、延迟之间做出精妙的平衡。本文将深入剖析 Linux 内核中两大核心分配器——Buddy System(伙伴系统)和 Slab Allocator,揭示它们的设计哲学、实现原理和工程实践。
二、Buddy System(伙伴系统)
2.1 设计哲学
Buddy System 采用分治策略:将物理内存按 2 的幂次划分为页面块(page block)。初始时,所有页面都在最大的块中。当需要分配 N 个页面时,系统从合适的阶(order)中取出一个块。如果该阶没有空闲块,就从更大的阶中分裂出一半。释放时,如果相邻的"伙伴"块也是空闲的,就合并成更大的块。
2.2 数据结构
struct free_area {
struct free_list free_list[MIGRATE_TYPES];
unsigned long nr_free;
};
struct zone {
struct free_area free_area[MAX_ORDER];
// ...
};
每个内存区域(zone)维护一个 free_area 数组,大小通常为 MAX_ORDER=11,对应页面数 1, 2, 4, 8, ..., 1024(即 4KB ~ 4MB)。每个 free_area 内又按迁移类型(migrate type)细分,这是 Linux 内核对抗外部碎片的关键创新。
2.3 分配流程
以 alloc_pages(gfp_mask, order) 为例:
- 快速路径:调用 __alloc_pages_nodemask(),先尝试从 pcper-cpu 高速缓存(page allocator hot path)中分配,这是 per-cpu 的 1-2 页小对象缓存。
- 慢速路径:若快速路径失败,进入伙伴系统核心:从当前 order 对应的 free_list 取块;若空,向更大 order 请求并递归分裂。
- 直接回收:若分配失败且允许 (__GFP_RECLAIM),触发页面回收(kswapd 或直接 reclaim)。
- OOM Killer:回收仍不足且设置了 __GFP_OOM,触发 OOM Kill 选择进程终止释放内存。
2.4 页面迁移类型(Migrate Types)
Linux 内核将 free_list 按迁移类型分区,从源头减少碎片:
- MIGRATE_UNMOVABLE:内核数据结构(页表、inode、dentry 等不可移动)。
- MIGRATE_MOVABLE:用户空间页面、可缓存内容(可回收)。
- MIGRATE_RECLAIMABLE:内核可回收对象(slab 缓存),不可移动但可以释放。
- MIGRATE_ISOLATE:用于热插拔和 compaction 期间的页面隔离。
- MIGRATE_CMA:连续内存分配器,为 GPU/DMA 保留大块连续物理内存。
2.5 外部碎片问题
伙伴系统保证分配出 2^order 大小的连续物理块,但长期运行后会产生外部碎片:虽然总空闲内存足够,但没有足够大的连续块。一个典型场景是频繁分配/释放不同 order 的页面后,小块散落各处无法合并。
三、页面压缩(Page Compaction)
3.1 原理
Linux 2.6.38 引入了页面压缩机制,由 kcompactd 内核线程异步运行。核心思想是在 MOVABLE 区域中,将可移动的页面集中到一个子区域,从而腾出大块连续物理内存。
// 简化逻辑
for each pageblock {
if (all pages movable in this block)
mark it as source
if (all pages free in this block)
mark it as target
}
// 迁移 source → target,释放连续大块
3.2 触发条件与调优参数
/proc/sys/vm/compaction_proactiveness:主动压缩的激进程度(0-100, 默认 20)。/proc/extfrag/unusable_index:每个 order 的不可用碎片指数,越低说明碎片越严重。/sys/kernel/mm/compaction/:手动触发echo 1

发表评论 取消回复