【Linux 内存管理】Kernel Buddy 分配器:Page Block Size 实现原理与工作机制深度解析

Kernel Buddy 分配器:Page Block Size 实现原理与工作机制深度解析

1. 基础概念

1.1 Page Block Size 定义

在 Linux 内核的 Buddy(伙伴)分配器中,Page Block Size(页面块大小)是一个逻辑上的内存管理单位,它定义了**迁移类型(Migrate Type)**管理的最小粒度。

虽然 Buddy 分配器可以分配小至 4KB(Order 0)的页面,但在进行内存碎片整理(Compaction)和反碎片化(Anti-fragmentation)策略时,内核是以 PageBlock 为单位进行管理的。每个 PageBlock 中的所有页面通常被标记为相同的迁移类型(如 MIGRATE_MOVABLE, MIGRATE_UNMOVABLE, MIGRATE_RECLAIMABLE)。

1.2 物理内存管理中的作用

Page Block Size 的引入主要为了解决 外部碎片(External Fragmentation) 问题:

  1. 反碎片化机制:通过将具有相同生命周期特性的页面(如均可移动的用户态匿名页)归类到同一个 PageBlock 中,内核可以避免不可移动的页面(如内核分配的页面)散落在可移动页面中间,从而阻碍大页(Huge Page)的分配。
  2. 内存规整(Compaction):当需要分配大块连续物理内存(如 HugeTLB)时,内存规整扫描器会以 PageBlock 为单位检查是否可以通过移动页面来腾出连续空间。

1.3 不同架构下的典型配置

Page Block Size 的大小通常由 pageblock_order 决定,其值依赖于架构和配置(特别是 HugeTLB 的配置)。

架构/配置 Page Size MAX_ORDER Page Block Order Page Block Size
x86_64 (Default) 4KB 11 9 (通常对应 2MB HugePage) 2MB
ARM64 (4KB Pages) 4KB 11 9 2MB
ARM64 (64KB Pages) 64KB 14 13 (对应 512MB HugePage) 512MB
No HugeTLB 4KB 11 MAX_ORDER - 1 (10) 4MB

注意pageblock_order 通常设定为 HugeTLB 页面的阶数(Order),这确保了内核能够有效地管理和分配大页。


2. 技术实现细节

2.1 核心数据结构

Page Block 的管理主要依赖于 struct zone 中的 pageblock_flags 位图。

struct zone (include/linux/mmzone.h)
c 复制代码
struct zone {
    /* ... */
#ifndef CONFIG_SPARSEMEM
    /*
     * Flags for a pageblock_nr_pages block. See pageblock-flags.h.
     * In SPARSEMEM, this map is stored in struct mem_section
     */
    unsigned long       *pageblock_flags;
#endif /* CONFIG_SPARSEMEM */
    /* ... */
};
pageblock_order 定义 (include/linux/pageblock-flags.h)
c 复制代码
#ifdef CONFIG_HUGETLB_PAGE
    #ifdef CONFIG_HUGETLB_PAGE_SIZE_VARIABLE
        extern unsigned int pageblock_order;
    #else
        #define pageblock_order     HUGETLB_PAGE_ORDER
    #endif
#else
    #define pageblock_order     (MAX_ORDER-1)
#endif

#define pageblock_nr_pages  (1UL << pageblock_order)

2.2 数据结构关系图

下图展示了 struct zonepage_mapPageBlock 之间的逻辑映射关系。

(图示说明:物理内存被划分为多个 PageBlock,每个 PageBlock 在 zone->pageblock_flags 位图中拥有对应的 bits 来存储其迁移类型)

2.3 核心代码路径

  1. 初始化
    • mm/page_alloc.c: free_area_init_core() -> setup_usemap(): 为 pageblock_flags 分配内存。
  2. 获取/设置类型
    • include/linux/pageblock-flags.h: get_pageblock_migratetype(page)
    • mm/page_alloc.c: set_pageblock_migratetype(page, migratetype)
  3. Fallback 机制
    • mm/page_alloc.c: __rmqueue_fallback(): 当指定类型的空闲页不足时,从其他类型的 PageBlock 借用内存,甚至改变整个 PageBlock 的类型。
关键函数调用关系 (Mermaid)

Order > 0 Order > 0 / Fallback __alloc_pages_nodemask get_page_from_freelist rmqueue __rmqueue_smallest __rmqueue_fallback Change PageBlock Type set_pageblock_migratetype Update zone->pageblock_flags


3. 工作机制分析

3.1 内存分割与合并

Buddy 分配器利用 Page Block Size 来隔离不同可移动性的页面。

  • 分割 :当请求一个 MIGRATE_MOVABLE 页面但该类型链表为空时,内核可能会从 MIGRATE_UNMOVABLE 的 PageBlock 中"偷取"页面。如果偷取的页面数量达到一定阈值(通常是一半以上),或者直接偷取了一个大块,整个 PageBlock 的属性可能会被修改为 MIGRATE_MOVABLE
  • 合并:释放页面时,Buddy 系统会尝试合并相邻的空闲页。如果合并后的块跨越了 PageBlock 边界(虽然物理上连续,但逻辑上属于不同管理块),通常不会改变 PageBlock 的属性,除非发生特定的大范围规整。

3.2 运行时状态转换示例

假设系统内存紧张,需要分配一个 MOVABLE 的页面:

  1. 初始状态
    • Block A (Order 9): MIGRATE_UNMOVABLE (内核数据使用)
    • Block B (Order 9): MIGRATE_MOVABLE (用户数据使用,已满)
  2. 分配请求
    • 请求 alloc_pages(GFP_HIGHUSER_MOVABLE, 0)
  3. Fallback 处理 (__rmqueue_fallback):
    • 扫描 MIGRATE_UNMOVABLE 列表。
    • 找到 Block A 中有空闲的大块内存。
    • 关键决策 :如果从 Block A 分配,是否改变 Block A 的类型?
      • 如果偷取的是大块(>= pageblock_order / 2),倾向于将整个 Block A 标记为 MIGRATE_MOVABLE
  4. 结果
    • Block A 变为 MIGRATE_MOVABLE。后续的内核不可移动分配将无法使用 Block A 中剩余的空间(除非再次发生 Fallback),从而保护了该 Block 能够逐渐形成大的连续可移动空间。

3.3 分配/释放处理流程

Yes No Yes Yes No No 开始 请求内存页 当前迁移类型
有空闲页? 从 buddy 链表摘除 进入 __rmqueue_fallback 扫描备用迁移类型列表 找到空闲块? 偷取页面块 偷取大小 >=
PageBlock一半? 修改 PageBlock
迁移类型属性 仅分配页面 结束 触发回收/OOM


4. 性能优化考量

4.1 Page Block Size 对碎片化的影响

Page Block Size 是内存反碎片化策略的基石。

  • 过小的 Page Block:导致不同迁移类型的页面混合得更细碎。例如,一个 2MB 的物理区域内可能混合了数个 64KB 的 Movable 和 Unmovable 块,这使得分配 2MB 大页几乎不可能。
  • 过大的 Page Block:导致内存区域属性转换困难。如果一个巨大的 Block 因为包含少量不可移动页而被锁定为 Unmovable,会浪费大量潜在的可移动空间。

4.2 性能权衡与测试数据

通常,Page Block Size 设置为与架构支持的最大 Huge Page 大小一致(如 x86 上的 2MB)是最佳实践。

下图展示了不同 Page Block 管理策略下的碎片化指数模拟对比:

(图示说明:在大内存分配场景下,匹配 HugePage 大小的 PageBlock 配置(如 2MB)能显著降低外部碎片指数,提高大页分配成功率)


5. 参考文献与环境信息

  • Kernel Version: Linux 4.4.94
  • Source Files :
    • mm/page_alloc.c
    • include/linux/mmzone.h
    • include/linux/pageblock-flags.h
  • References :
    • Gorman, Mel. "Understanding the Linux Virtual Memory Manager."
    • Linux Kernel Documentation: Documentation/vm/page_migration

图表数据基于模拟场景。*

相关推荐
HLC++11 小时前
Linux的进程间通信
android·linux·服务器
华清远见IT开放实验室12 小时前
实验室建设案例 | 石家庄科技信息职业学院嵌入式实验室——从底层硬件到系统应用,一所应用型高校的嵌入式人才培养这样落地
linux·arm开发·stm32·嵌入式硬件·高校·实验室建设
groundhappy15 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕16 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫17 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生17 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1118 小时前
linux基础IO
linux·运维·服务器
BelongPanda20 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯20 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_7779983421 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器