Linux中内存管理子系统使用节点(node)、区域(zone)和页(page)三级结构描述物理内存。
内存节点
内存节点分两种情况:UMA
和NUMA
。
从管理内存的方法上区分,计算机可以分为两种类型:UMA
和NUMA
。
UMA
:一致性内存访问,uniform memory access
NUMA
:非一致性内存访问,non-uniform memory access
两种类型示意图:
对UMA
来说,每一个CPU
访问的都是同一块内存,因此各CPU
对内存的访问不存在性能差异
对NUMA
来说,各内存和各CPU
通过总线连在一起,每个CPU
都有一个本地内存,访问速度快,CPU
也可以访问其他CPU
的本地内存,但速度稍慢
Linux为了统一这两种平台,在内存组织中,将最高层次定义为内存节点.
可以看到,图中UMA
只有一个内存节点,而NUMA
有两个内存节点。
实际上,UMA
其实是NUMA
的一个特例,所以内核可以将内存都看做NUMA
类型的。
区域
每个内存节点都划分为多个区,Linux内核中定义了以下几个区:
include/linux/mmzone.h
enum zone_type{
#ifdef CONFIG_ZONE_DMA
ZONE_DMA,
#endif
#ifdefi CONFIG_ZONE_DMA32
ZONE_DMA32,
#endif
ZONE_NORMAL,
#ifdef CONFIG_HIGHMEM
ZONE_HIGHMEM,
#endif
ZONE_MOVABLE,
#ifdef CONFIG_ZONE_DEVICE
ZONE_DEVICE,
#endif
__MAX_NR_ZONES
};
ZONE_DMA
DMA
是“Direct Memory Access
”的缩写,直接内存访问。
该区域用于ISA设备的DMA操作,范围是0-16MB。
如果有些设备不能直接访问所有内存,则需要使用DMA
区域。例如旧的工业标准体系结构(Industry Standard Architecture, ISA
)总线只能直接访问16MB以下的内存。
只适用于Intel x86架构,ARM架构没有这个内存管理区。
ZONE_DMA32
在64位的系统上使用32位地址寻址的适合DMA操作的内存区。
例如在AMD64系统上,该区域为低4GB的空间。在32位系统上,本区域通常是空的。
ZONE_NORMAL
常规内存区,指的是可以直接映射到内核空间的内存。
也常称为“普通区域”“直接映射区域”“线性映射区域”。
所谓线性映射就是物理地址和映射后的虚拟地址存在一种简单的关系,即虚拟地址=物理地址+固定偏移。
在32位系统上,内核空间和用户空间按1:3划分,那么这个固定偏移就是:0xC0000000 - 物理内存起始地址。
既然存在一种线性关系,那还需要使用页表对物理地址和虚拟地址做映射吗?
不同处理器架构实现不一样,ARM需要使用页表映射,MIPS则不需要。
ZONE_HIGHMEM
高端内存区,32位时代的产物。在32位系统上,指的是高于896M
的物理内存。
32位系统中,内核和用户地址空间按1:3划分,内核地址空间只有1GB,所以不能把1GB以上的内存直接映射到内核地址空间,因此就把不能直接映射的内存划分到了高端内存区。
要将高于896MB的物理内存映射在内核空间的话,需要通过单独的映射来完成,并且这类映射不能保证物理地址和虚拟地址之间存在固定的对应关系(例如ZONE_NORMAL的固定偏移)。
ZONE_DMA、ZONE_DMA32、ZONE_NORMAL通常都统称为低端内存区。
64位系统中没有这个区域,即没有高端内存。因为64系统的内核虚拟地址空间非常大,不再需要高端内存区域。
ZONE_MOVABLE
一个伪内存区,用来防止内存碎片。
ZONE_DEVICE
为支持持久内存(persistent memory)热拔插增加的内存区域
页
站在处理器的角度来看,管理物理内存的最小单位是页面。
现在的处理器都采用分页机制来管理内存,在处理器内部有一个MMU
硬件,它会处理虚拟内存到物理内存的映射,也就是做页表的翻译工作。
Linux
内核中使用一个page
数据结构来描述一个物理页面。
页的大小通常是4KB,但有个的架构的处理器可以支持大于4KB
的页,例如8KB
、16KB
或者64KB
的页。
目前Linux内核默认使用4KB的页面。
所以,Linux内核的用三级结构来管理物理内存,简言之就是内存首先划分成若干个大的节点,每个节点又包含若干个区,每个区有包含若干页。Linux内核按页管理内存,最基本的内存分配和释放都是按页进行的。