一、引言

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) 为例:

  1. 快速路径:调用 __alloc_pages_nodemask(),先尝试从 pcper-cpu 高速缓存(page allocator hot path)中分配,这是 per-cpu 的 1-2 页小对象缓存。
  2. 慢速路径:若快速路径失败,进入伙伴系统核心:从当前 order 对应的 free_list 取块;若空,向更大 order 请求并递归分裂。
  3. 直接回收:若分配失败且允许 (__GFP_RECLAIM),触发页面回收(kswapd 或直接 reclaim)。
  4. 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
点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部