1. 内存管理概述:物理内存与虚拟内存、内存碎片化问题、伙伴系统诞生的背景

1.1 物理内存与虚拟内存:两大核心抽象

内存管理是现代操作系统内核的基石。为了在有限的物理内存上运行多个进程,并保证进程间的隔离与安全,内核引入了物理内存与虚拟内存两大抽象层。

特性 物理内存 (Physical Memory) 虚拟内存 (Virtual Memory)
定义 硬件实际提供的 RAM 存储单元,由内存控制器直接寻址。 操作系统为每个进程提供的独立、连续的地址空间抽象。
寻址方式 物理地址 (PA),由 CPU 地址总线直接发出。 虚拟地址 (VA),通过 MMU 页表转换为物理地址。
空间大小 受限于硬件插槽与 CPU 寻址能力(如 4GB、64GB)。 受限于 CPU 位宽(32 位为 4GB,64 位理论可达 16EB)。
核心作用 存放实际运行的代码与数据,是系统的“硬通货”。 提供隔离、简化编程、支持按需分页与交换。
管理粒度 通常以页框 (Page Frame) 为单位,大小固定(如 4KB)。 以页 (Page) 为单位,与物理页框通过页表映射。
关键理解: 虚拟内存让每个进程以为自己独享连续的大地址空间,而物理内存则负责在幕后高效地分配和回收这些宝贵的硬件资源。内存管理的核心任务之一,就是管理物理内存的分配与释放,而 伙伴系统 (Buddy System) 正是 Linux 内核用于管理物理内存页框的核心算法。

1.2 内存碎片化问题:内核的“心头大患”

随着系统长时间运行,频繁的分配与释放操作会导致物理内存出现碎片化。碎片化是伙伴系统诞生的直接驱动力。它主要分为两类:

  • 外部碎片 (External Fragmentation): 虽然总空闲内存足够,但没有一块连续的物理内存区域能满足一个大块分配请求。例如,系统有 10MB 空闲内存,但分散成 100 个 100KB 的小块,无法满足一个 1MB 的连续分配请求。
  • 内部碎片 (Internal Fragmentation): 分配器给请求者分配了比实际需求更大的内存块,导致块内未被使用的空间被浪费。例如,请求 3KB,分配器按最小粒度 4KB 分配,浪费了 1KB。
碎片化的危害: 在长期运行的服务器或嵌入式系统中,严重的外部碎片可能导致内核无法分配连续的 2^order 页框,即使总内存充裕,也会触发 OOM (Out-Of-Memory) 或导致 DMA 等需要连续物理内存的操作失败。
物理内存碎片化示意图 物理内存碎片化示意 已分配页框 空闲页框 理想状态: 已分配 大块连续空闲 碎片化状态: A F1 B F2 C F3 问题: 总空闲内存 = F1(40KB) + F2(60KB) + F3(100KB) = 200KB 但无法分配一个 150KB 的连续块,因为最大连续空闲块只有 100KB。

1.3 伙伴系统诞生的背景:对抗碎片化的经典方案

在伙伴系统出现之前,早期操作系统曾使用过多种内存分配算法,但都存在明显缺陷:

  • 动态分区分配(如首次适应、最佳适应): 管理灵活,但会产生大量无法合并的外部碎片,且需要复杂的链表维护。
  • 固定分区分配: 将内存划分为大小固定的分区,实现简单,但内部碎片严重,且无法支持大小不一的请求。

1963 年,Kenneth C. Knowlton 提出了伙伴系统 (Buddy System) 算法,旨在平衡分配效率与碎片防治。其核心思想是:

  1. 以 2 的幂次方划分内存: 将物理内存视为大小为 2^N 页框的连续块。
  2. 按需分裂: 当请求大小为 2^(n-1) < request ≤ 2^n 时,从 2^n 的空闲块中分配;若没有,则从更大的块中分裂出两个大小相等的“伙伴”块,直到满足需求。
  3. 伙伴合并: 当释放一个块时,检查其“伙伴”(即由同一父块分裂出的另一半)是否也为空闲。如果是,则立即合并回更大的块,并递归向上合并。
核心优势: 伙伴系统通过“分裂”快速响应分配请求,通过“合并”主动逆转碎片化过程。这种“分而治之,合而为一”的机制,使得外部碎片被限制在最小粒度(通常为 4KB),且合并操作的时间复杂度为 O(log N),非常高效。
伙伴系统分裂与合并流程 伙伴系统:分裂与合并流程 初始 2^3 块 (32KB) 分裂 (Split) 伙伴 A (16KB) 伙伴 B (16KB) 分裂 A1 (8KB) A2 (8KB) 已分配 释放并检查伙伴 A2 合并回伙伴 A (16KB) 合并条件: 1. 两个块大小相同。 2. 物理地址连续。 3. 由同一个父块分裂而来。 4. 两个块都处于空闲状态。 → 合并为更大的空闲块

1.4 为什么 Linux 选择伙伴系统?

Linux 内核在众多内存分配算法中选择了伙伴系统作为物理页框分配器的核心,主要基于以下考量:

需求 伙伴系统的优势
高效分配与释放 分裂与合并操作的时间复杂度均为 O(log N),其中 N 为最大内存块阶数,性能稳定可预测。
防治外部碎片 通过“伙伴合并”机制,主动将小空闲块合并成大块,有效缓解了长期运行下的外部碎片问题。
支持多种块大小 天然支持 2 的幂次方大小的分配请求(从 4KB 到 4MB 甚至更大),满足内核不同子系统的需求。
实现简单且健壮 核心数据结构为多个空闲链表(每个阶一个),逻辑清晰,易于维护和调试。
与硬件缓存友好 分配连续物理页框有助于利用 CPU 缓存 (Cache) 的预取机制,提升性能。
课程预告: 在后续章节中,我们将深入剖析 Linux 内核中伙伴系统的具体实现,包括 struct free_area 数据结构、__alloc_pages_nodemask() 分配路径、以及 __free_one_page() 合并逻辑的完整源码分析。同时,我们还将探讨伙伴系统无法解决的 内存碎片化 问题,并介绍 迁移类型 (MIGRATE_TYPES)、虚拟可移动区域 (ZONE_MOVABLE) 和 内存规整 (Memory Compaction) 等高级防治技术。