【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

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

相关推荐
robin59112 小时前
容器-汇总所有环境下的日志排查问题
linux·容器·kubernetes
朗晴2 小时前
Linux修改SSH远程端口号22!
linux·运维·ssh
赖small强3 小时前
【Linux 驱动开发】Linux PWM (脉冲宽度调制) 全面技术指南
linux·驱动开发·pwm
代码游侠3 小时前
Linux系统编程 - 文件操作
linux·运维·服务器·学习
Web极客码3 小时前
CentOS与RHEL安装EPEL源解析错误修复
linux·centos·php
杰哥技术分享3 小时前
宿主机(CentOS)没有安装 PHP,但想使用php
linux·centos·php
Xの哲學3 小时前
Linux I3C驱动深度剖析: 从原理到实战的全面解析
linux·服务器·算法·架构·边缘计算
赖small强4 小时前
【Linux 进程管理】Linux 可执行程序运行机制深度解析
linux·可执行程序
casdfxx4 小时前
配置v3s支持8188eu、8192cu网卡(三)-openssh不能登录linux开发板。
linux·服务器·网络