Linux 内存管理窥探(2):内存模型

讨论内存的模型之前需要先聊一下两种计算机架构:UMA 和 NUMA

UMA 称为均匀存储器存取(Uniform-Memory-Access) : 

物理存储器被所有处理机均匀共享。所有处理机对所有存储字具有相同的存取时间,这就是为什么称它为均匀存储器存取的原因。每台处理机可以有私用高速缓存,外围设备也以一定形式共享。

NUMA 非均匀存储器存取(Nonuniform-Memory-Access):

其访问时间随存储字的位置不同而变化。其共享存储器物理上是分布在所有处理机的本地存储器上。所有本地存储器的集合组成了全局地址空间,可被所有的处理机访问。处理机访问本地存储器是比较快的,但访问属于另一台处理机的远程存储器则比较慢,因为通过互连网络会产生附加时延。 

一个简单的图形可以更加形象的解释这两种构建的不同之处,

对于 UMA 模型:

对于 NUMA 模型:

 

Linux 内存结构组织

Linux适用于各种不同的体系结构, 而不同体系结构在内存管理方面的差别很大. 因此linux内核需要用一种体系结构无关的方式来表示内存。Linux内核通过插入一些兼容层, 使得不同体系结构的差异很好的被隐藏起来, 内核对一致和非一致内存访问使用相同的数据结构。

非一致存储器访问(NUMA)模式下:

1. 处理器被划分成多个”节点”(node), 每个节点被分配有的本地存储器空间. 所有节点中的处理器都可以访问全部的系统物理存储器,但是访问本节点内的存储器所需要的时间,比访问某些远程节点内的存储器所花的时间要少得多。

2. 内存被分割成多个区域(BANK,也叫”簇”),依据簇与处理器的”距离”不同, 访问不同簇的代码也会不同. 比如,可能把内存的一个簇指派给每个处理器,或则某个簇和设备卡很近,很适合DMA,那么就指派给该设备。因此当前的多数系统会把内存系统分割成2块区域,一块是专门给CPU去访问,一块是给外围设备板卡的DMA去访问
 

Linux把物理内存划分为三个层次来管理:

存储的节点(node): CPU 划分为多个节点(node),每个 CPU 对应一个本地内存

管理区(zone):每个物理内存节点(node)被划分为多个管理区(zone)

页面(page):每个管理区被更加细分为页面(page)进行管理,page 也是最小的管理单元

 

内存节点(node)

每一个 node 对应一个 CPU,系统物理内存被划分为若干个 node。在内核中,使用 pg_data_t 的数据结构对一个内存 node 进行描述。多个 pg_data_t 被链接到一个 pglist_data 的链表中,表征了所有的物理内存节点。

注意:

对于NUMA系统来讲,整个系统的内存由一个 node_data  指针数组管理,可以使用NODE_DATA(node_id)来查找系统中编号为node_id的结点

extern pg_data_t *node_data[];

对于 UMA 结构的机器,只是用了一个静态的 struct pglist_data contig_page_data 来管理内存:

struct pglist_data __refdata contig_page_data;

pg_data_t 的定义在 linux/mmzone.h 中有兴趣可以研究一下。

 

物理内存区域(zone)

内存区域(zone)是属于单个内存节点中的概念。

因为实际的计算机体系结构有硬件的诸多限制,这限制了页框可以使用的方式。尤其是, Linux内核必须处理 80x86 体系结构的两种硬件约束。

  • ISA总线的直接内存存储 DMA 处理器有一个严格的限制 : 他们只能对RAM的前16MB进行寻址
  • 在具有大容量RAM的现代32位计算机中, CPU不能直接访问所有的物理地址, 因为线性地址空间太小, 内核不可能直接映射所有物理内存到线性地址空间

因此Linux内核对不同区域的内存需要采用不同的管理方式和映射方式,

为了解决这些制约条件,Linux使用了三种区:

ZONE_DMA : 这个区包含的页用来执行DMA操作。

ZONE_NOMAL : 这个区包含的都是能正常映射的页。

ZONE_HIGHEM : 这个区包”高端内存”,其中的页能不永久地映射到内核地址空间

 

而为了兼容一些设备的热插拔支持以及内存碎片化的处理, 内核也引入一些逻辑上的内存区.

ZONE_MOVABLE : 内核定义了一个伪内存域ZONE_MOVABLE, 在防止物理内存碎片的机制memory migration中需要使用该内存域. 供防止物理内存碎片的极致使用

ZONE_DEVICE : 为支持热插拔设备而分配的Non Volatile Memory非易失性内存

 

一个管理区(zone)由 struct zone  结构来进行表示。zone 被嵌入到了 pglist_data 在结构中,以数组的方式保存(多个 zone 区)

struct bootmem_data;
typedef struct pglist_data {
	struct zone node_zones[MAX_NR_ZONES];
	struct zonelist node_zonelists[MAX_ZONELISTS];
	int nr_zones;
#ifdef CONFIG_FLAT_NODE_MEM_MAP	/* means !SPARSEMEM */
	struct page *node_mem_map;
#ifdef CONFIG_PAGE_EXTENSION
	struct page_ext *node_page_ext;
#endif
#endif
......
}

struct zone  的定义在 linux/mmzone.h 中有兴趣可以研究一下。

 

物理内存页(page)

页面是 Linux 内核管理物理内存的最小单位,以 page 来进行管理,通常的,一个 page 的大小等于 4K。

每个物理的页由一个 struct page 结构体来表示

页的数据结构对象都保存在 mem_map 全局数组中,该数组通常被存放在ZONE_NORMAL的首部,或者就在小内存系统中为装入内核映像而预留的区域之后。从载入内核的低地址内存区域的后面内存区域,也就是ZONE_NORMAL开始的地方的内存的页的数据结构对象,都保存在这个全局数组中

struct page 的定义,在 mm_types.h

 

综上所述:这几个结构的关系为: