Linux内存管理章节五:Linux物理内存管理的核心:伙伴系统深入分析

引言

在上一篇中,我们概述了伙伴系统作为内核"页分配器"的角色。它从底层负责分配连续的物理页框。但物理内存并非一个 homogeneous(均匀)的整体。不同的硬件体系结构对内存的使用有特殊限制,而伙伴系统必须巧妙地处理这些限制,同时还要应对内存碎片化的永恒挑战。今天,我们将深入伙伴系统的内部,探索其如何通过内存区域(Zones) 的分类管理、精巧的分配算法 以及应对碎片化的策略,来高效地管理物理内存。

一、 内存区域(Zones):因地制宜的内存管理

为什么物理内存需要分区?原因在于硬件的异构性。并非所有物理内存对内核的所有部分都是平等的。伙伴系统将物理内存划分为不同的区域(Zones),主要是为了应对以下两种硬件限制:

  1. 硬件设备DMA的限制:古老的ISA总线设备在进行DMA(直接内存访问)时,只能访问物理内存的前16MB空间。
  2. CPU物理地址寻址限制:在32位系统上,CPU无法通过线性映射直接访问所有物理内存(参见上一篇关于HIGHMEM的讨论)。

Linux内核主要定义了以下三种区域(具体架构可能略有不同):

| 区域 | 目的 | 物理范围 (典型32位x86) | 说明 |

| :--- | :--- | :--- | :--- |

| ZONE_DMA | 专供DMA使用 | 0 ~ 16MB | 满足那些无法访问高地址内存的老式设备的DMA操作需求。 |

| ZONE_DMA32 | 供32位设备DMA使用 | 16MB ~ 4GB | 在64位系统上,为只能访问32位地址的设备提供DMA内存。 |

| ZONE_NORMAL | 正常内核映射 | 16MB ~ 896MB | 最重要的区域 。内核直接线性映射的区域,大部分内核分配发生于此,访问速度最快。 |

| ZONE_HIGHMEM | 高端内存 | 896MB ~ 结束 | 仅存在于32位系统 。用于动态映射超过ZONE_NORMAL的物理内存。64位系统无此区域。 |

工作方式

伙伴系统不是 只有一个全局的空闲链表。而是为每个内存区域(Zone)都维护一套独立的伙伴系统数据结构 (即一组free_area链表)。当内核发起分配请求时,它会指定一个** GFP(Get Free Pages)标志**(如GFP_KERNELGFP_DMA),分配器会根据这个标志决定从哪个Zone中分配内存。

  • 例如:一个设备驱动程序需要为DMA操作分配内存,它会使用GFP_DMA标志。伙伴系统就会只在ZONE_DMA中为其寻找空闲页框,确保设备能够访问这块内存。

(这是一个简化的示意图,实际物理布局可能因架构和配置而异)

二、 页框分配与回收算法

伙伴系统的核心是它的分配和释放(回收)算法,其精髓在于分裂合并

分配过程(以分配4个连续页为例,order=2
  1. 指定区域 :分配请求携带gfp_mask,确定备选Zone的优先级(如优先从ZONE_NORMAL分配)。
  2. 查找空闲链表 :伙伴系统从当前Zone的free_area[2]链表中查找是否有大小为4页的空闲块。
  3. 递归分裂(如果未找到)
    • 如果order=2的链表为空,则向上查找order=3的链表(8页的块)。
    • 如果找到一个8页的块,将其分裂 成两个伙伴(Buddy) 块(各4页)。
    • 将其中一个4页块插入free_area[2]链表,另一个用于满足分配请求。
    • 如果order=3也没有,则继续向上查找和分裂,直到最大order。
  4. 水位检查与回收 :如果所有Zone的所需order都无法满足分配,分配器会触发内存回收(kswapd),尝试释放一些内存,然后再重试。如果回收后仍无法满足,可能触发OOM Killer。
释放与合并过程
  1. 释放 :当释放4个页(order=2)时,系统将其放回对应Zone的free_area[2]链表。
  2. 查找伙伴 :系统会计算被释放块的伙伴块 的地址。伙伴块的特点是:大小相同,且物理地址连续,其起始地址在二进制上只有第(order+1)位不同。
  3. 检查并合并
    • 检查计算出的伙伴块是否存在空闲位于同一个Zone
    • 如果所有条件满足,则将这两个order=2的伙伴块从链表中移除,合并 成一个order=3的更大块。
    • 这个合并过程会递归进行 :系统会尝试继续合并新生成的order=3块和它的伙伴,直到无法合并为止。
  4. 挂入新链表:将最终合并成的大块插入到对应order的空闲链表中。

合并机制是伙伴系统对抗外部碎片的最强大武器,它尽可能地让零散的小块内存组合成连续的大块。

三、 碎片化问题与解决方案

尽管伙伴系统通过合并有效减少了外部碎片( scattered free memory),但内存碎片化仍然是系统长期运行后性能下降的主要原因之一。Linux内核引入了多种技术来缓解此问题。

1. 反碎片(Anti-Fragmentation)技术

内核在启动时就对页框进行了分类,根据其可移动性(Mobility) 组织到不同的伙伴系统链表中:

  • 不可移动页(Unmovable):内核核心代码、大部分内核数据结构、 DMA缓冲区等。它们物理位置必须固定。
  • 可回收页(Reclaimable):用户数据页、文件缓存等。它们可以被写入磁盘后再回收。
  • 可移动页(Movable):用户态进程的堆栈空间、页缓存等。它们可以被移动到新的物理位置。

工作原理

伙伴系统为每种可移动类型都建立了独立的free_area链表 。当需要分配一个不可移动的页时,它只会从不可移动页的链表中分配,而不会去占用一个本可用于移动页的块。这样,相同类型的页倾向于聚集在一起

好处

即使系统运行很久,不可移动的页也不会分散在各个角落,从而"污染"大块的连续空闲区域。可移动页所占用的巨大连续空间依然保持完整,可以被大块分配请求使用,或者为内存规整提供条件。

2. 内存规整(Memory Compaction)

当系统确实因为碎片无法分配大块连续内存时,内核会触发一个名为内存规整的后台进程。

  • 过程 :它的工作原理类似于磁盘碎片整理。它将可移动的页 从一块物理内存区域迁移到别处,从而在原位置创造出更大的连续空闲块
  • 触发条件:通常由伙伴系统分配高阶(high-order)内存失败时触发。
  • 依赖 :内存规整的有效性高度依赖于反碎片技术。只有页被正确分类,规整才知道哪些页可以安全地移动。
3. 虚拟可移动内存块(CMA:Contiguous Memory Allocator)

这是应对碎片问题的"终极大招",主要用于嵌入式系统为大容量DMA设备(如GPU、摄像头)预留内存。

  • 原理 :在系统启动时,预先保留一大块连续的物理内存区域。
  • 巧妙之处 :在系统未使用CMA时,这块保留内存可以被内核分配给可移动页 使用(例如普通应用程序)。当设备驱动程序需要分配大块连续DMA内存时,CMA通过迁移这些可移动页,腾出这块保留区,从而快速满足分配请求。
  • 优点:避免了在启动时就永久占用大量内存,提高了内存利用率,同时又保证了在需要时能提供可靠的连续内存。

总结

伙伴系统远不止是一个简单的"分裂与合并"算法。它是一个复杂的内存管理框架,通过:

  1. 分区管理(Zones):应对硬件限制。
  2. 精巧的分配/回收算法:高效处理各种大小的请求。
  3. 反碎片与内存规整:动态对抗外部碎片,保持内存的可用性。
  4. CMA:为特殊需求提供保证。

这些机制共同工作,使得Linux内核能够在各种硬件配置和负载下,高效、可靠地管理物理内存这一宝贵资源。理解这些底层机制,有助于我们更好地分析系统内存状态(如查看/proc/buddyinfo)和调试复杂的内存问题。

相关推荐
广药门徒10 小时前
正点原子LINUX imx6ull开发板的nfs传输配置(传输慢,失败等问题)
linux·运维·服务器
笑口常开xpr11 小时前
惊!printf 不往屏幕输?都是 fd 在搞鬼!爆肝拆解 Linux 文件描述符 + 重定向底层,学会直接在终端横着走
linux·文件·重定向
一RTOS一11 小时前
东土正创AI交通服务器再获北京市批量应用订单
运维·服务器·人工智能
群联云防护小杜11 小时前
服务器异常负载排查手册 · 隐蔽进程篇
运维·服务器·前端·数据库·笔记·sql·tcp/ip
aspirestro三水哥11 小时前
服务器研发的历史变迁:从通用到定制化
运维·服务器
挺6的还11 小时前
22.Linux进程信号(三)
linux
☆璇11 小时前
【Linux】Linux权限
linux·运维·服务器
二进制coder12 小时前
Linux内存管理章节三:绘制Linux的内存地图:内核与用户空间布局详解
linux·运维·网络
2501_9200470312 小时前
Linux-xargs-seq-tr-uniq-sort
linux·运维·服务器