准备

内核版本: 4.20.1

在深入了解Linux物理内存管理之前,我们需要对物理内存的组织形式有一个初步的了解。Linux内核将物理内存简单的分为两部分: 用户物理内存空间和内核物理内存空间。当用户进程需要访问物理内存时,通过虚拟地址映射到用户的物理内存空间。这篇博客简单的介绍物理内存组织的相关概念和数据结构。

两种内存访问架构

UMA架构

将所有的物理内存以连续的方式组织起来。

NUMA架构

在多处理器计算机中,每个CPU都拥有本地内存,可以支持更快的访问速度,但CPU访问其他CPU的本地内存时,需要通过总线传输,从而速度慢些。

numa

物理内存布局

在每一个进程看来,它是独占整个内存空间的,这是因为Linux采用了虚拟内存的技术来管理每一个进程的内存空间,但其实每个进程的使用的内存都能映射到不同的物理内存空间。虚拟内存的布局涉及代码段,数据段,堆栈等等,但这里我们讨论的是物理内存空间布局,即无关虚拟内存的抽象。

virtual_physics

Linux 把物理内存划分为 3 个层次来管理: 存储节点(Node), 管理区(Zone)和页面(Page),并用 3 个相应的数据结构来描述, 在NUMA架构中一个Node就代表一个CPU直接访问的本地内存区域,而在非NUMA架构即UMA架构中,Node的个数为1.

physics_level

上图为Node, Zone和Page的映射关系.

Zone的类型

物理内存被区分为以下几种常见的类型:

  • ZONE_DMA: DMA的内存区域, 主要是各种设备驱动使用的内存区域.
  • ZONE_DMA32: 在x86_64架构下,需要ZONE_DMA32,因为需要32位地址寻址的DMA.
  • ZONE_NORMAL: 可以直接映射的普通内存区域.
  • ZONE_HIGHMEM: 在32 bit的架构中,ZONE_HIGHMEM是需要的,因为32 bit的计算机最多只能映射2^32 即4GB的物理内存,假如计算机使用了更大的物理内存条,需要借助ZONE_HIGHMEM 来访问更大的内存区域.

x86的zone初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void __init zone_sizes_init(void)
{
unsigned long max_zone_pfns[MAX_NR_ZONES];

memset(max_zone_pfns, 0, sizeof(max_zone_pfns));

#ifdef CONFIG_ZONE_DMA
max_zone_pfns[ZONE_DMA] = min(MAX_DMA_PFN, max_low_pfn);
#endif
#ifdef CONFIG_ZONE_DMA32
max_zone_pfns[ZONE_DMA32] = min(MAX_DMA32_PFN, max_low_pfn);
#endif
max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
#ifdef CONFIG_HIGHMEM
max_zone_pfns[ZONE_HIGHMEM] = max_pfn;
#endif

free_area_init_nodes(max_zone_pfns);
}

x86_64物理内存布局

目前x86_64支持的最大的寻址长度是48,即2^48 256TB的物理内存长度,所以暂时不需要ZONE_HIGHMEM:

x86_64