Linux 物理内存外碎片化浅析

本文出现的内核代码来自linux4.19,如果有兴趣,读者可以配合代码阅读本文 。
一、Linux物理内存外碎片化概述
什么是Linux物理内存碎片化?Linux物理内存碎片化包括两种:
1.物理内存内碎片:指分配给用户的内存空间中未被使用的部分 。
例如进程需要使用3K bytes物理内存,于是向系统申请了大小等于3Kbytes的内存,但是由于Linux内核伙伴系统算法最小颗粒是4K bytes,所以分配的是4Kbytes内存,那么其中1K bytes未被使用的内存就是内存内碎片 。

Linux 物理内存外碎片化浅析

文章插图
 
Linux物理内存内碎片
2.物理内存外碎片化:指系统中无法利用的小内存块 。
例如系统剩余内存为16K bytes,但是这16K bytes内存是由4个4K bytes的页面组成,即16K内存物理页帧号#1不连续 。在系统剩余16K bytes内存的情况下,系统却无法成功分配大于4K的连续物理内存,该情况就是内存外碎片导致,本文中阐述的就是物理内存外碎片化 。
注:#1物理页帧号:Linux物理内存是通过页面进行管理,并对每个页面进行编号,称为页帧号,如果是连续的两个物理页面,其页帧号是连续的 。
Linux 物理内存外碎片化浅析

文章插图
 
Linux物理内存外碎片
二、Linux物理内存管理框架
阐述物理内存外碎片化的来龙去脉前,先得明白Linux是如何管理物理内存的?Linux内核采用的是buddy system allocation,即著名的伙伴系统分配器 。
1.设计思路
伙伴系统分配器的核心思路:将系统的空闲页面分为11个块链表,每个块链表分别管理着1,2,4,8,16,32,64,128,256,512和1024个物理页帧号连续的页面 。每个页面大小为4K bytes,buddy管理的块大小范围从4K bytes到4M bytes,以2的倍数递增 。
Linux 物理内存外碎片化浅析

文章插图
 
Linux物理内存管理框架图
2.管理逻辑
Linux对物理页面管理的框架如上图,由于本文阐述的是物理内存外碎片,所以关于伙伴系统本文只做简单分析,不涉及具体的细节并不阐述关于per cpu pageset等内容,如果读者有兴趣,可以参考内核源码 。
Linux将物理内存分为不同的node和zone来管理:
  • node:为了支持NUMA结构,即CPU对不同内存簇的访问速度不同,Linux设计了node结构,将物理内存分为多个内存节点管理;对于UMA结构,只有一个node节点 。
  • zone:为兼容不同的平台的硬件限制,例如80x86的体系结构的硬件总线访问等问题,Linux将node节点下的内存分为多个zone;目前在ARM平台,多个zone管理已非必要 。
zone管理单元下的内存通过free_area数组将内存分成11个块链表进行管理:
Linux 物理内存外碎片化浅析

文章插图
 
free_area数组总共有11个索引,每个索引管理着不同大小的块链表 。
  • free_area[0]管理的内存单位为2^0页面,即4K byte内存;
  • free_area[1]管理的内存单位为2^1的物理页帧号连续页面,即8K bytes内存;
  • 以此类推;
free_area管理的内存还细分为各种类型,例如不可移动页面和可移动页面等,每种类型的页面类型对应一个free_list链表,该链表就链接着页面结构体 。
Linux 物理内存外碎片化浅析

文章插图
 
当分配页面时,伙伴系统拿页面的步骤如下:(不考虑内存慢速路径)
  • 根据分配页面类型,找到对应的内存节点node和内存管理单元zone;
  • 根据分配页面大小,找到的对应大小的free_area结构体;
  • 根据分配页面类型,找到对应的free_list链表,分配页面;
当向伙伴系统释放页面时,buddy释放页面的步骤如下:
  • 根据分配页面类型,找到对应的内存节点node和内存管理单元zone;
  • 判断是否有物理页帧号相连的空闲内存块,可以跟被释放的内存块合并成更大的块内存,合并的条件:
  • 物理帧必须都是连续的;
  • 相同的类型和相同的大小;
  • 合并后块内存的第一个页面的物理地址满足”2*块大小*4K”的倍数 。
  • 根据释放页面的大小或者合并的大小,找到的对应大小的free_area结构体;
  • 根据释放页面的类型,找到对应的free_list链表,释放页面;
三、Linux针对物理内存外碎片化的措施
从“二、Linux物理内存管理架构”,可以发现伙伴系统内存管理框架是可以有效改善物理内存外碎片的,因为伙伴系统有如下两个管理逻辑,可以减少了外碎片化的产生:


推荐阅读