0%

Linux 内存模型简析

memory model

在 Linux 内核中支持3种内存模型,分别为:

  • flat memory model(平坦内存模型)
  • discontiguous memory model(不连续内存模型)
  • sparse memory model(稀疏内存模型)

所谓 memory model(内存模型),其实就是从 CPU 的角度看其物理内存的分布情况,代表了在 Linux Kernel 中,使用什么的方式来管理这些物理内存(某些体系架构支持多种内存模型,但在内核编译构建时只能选择使用一种内存模型)

  • 程序分段和 CPU 内存分段是不同的概念

include/asm-generic/memory_model.h 中,Linux 为每个 memory model 准备了如下的宏定义:

1
2
#define page_to_pfn __page_to_pfn /* 虚拟内存->物理内存 */
#define pfn_to_page __pfn_to_page /* 物理内存->虚拟内存 */
  • 用于把虚拟内存中的 struct page 和切分后的物理内存 page frame 关联起来

Flat memory model(平坦内存模型)

平坦内存模型是相对于多段模型而言的

  • 8086实模式CPU的16位寄存器最多只能在一个内存段的64kb空间内寻址,要是超过64kb,它只能先变换段基址,来达到长距离取指的目的
  • 平坦模型至始至终只有一个段,它能直接访问内存空间,不用再进行段基址的变换
1
2
3
#define __pfn_to_page(pfn)	(mem_map + ((pfn) - ARCH_PFN_OFFSET))
#define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + \
ARCH_PFN_OFFSET)

图示:

  • 对于 flat memory model 来说,物理内存本身是连续的
  • 如果不连续的话,那么中间一部分物理地址是没有对应的物理内存,就会形成一个个洞,这就浪费了 mem_map 数组本身占用的内存空间

其特点如下:

  • 内存连续且不存在空隙
  • 通常应用于 UMA 系统(一致内存访问)
  • 通过 CONFIG_FLATMEM 进行配置

Discontiguous memory model(不连续内存模型)

如果CPU在访问物理内存的时候,其地址空间是有一些空洞的,是不连续的,那么这种计算机系统的内存模型就是 discontiguous memory model

  • discontiguous memory model 是为了 NUMA 系统设计的(非一致内存访问)
  • NUMA 系统中每个 CPU 都有自己的本地内存,CPU 访问本地内存不用过总线,因而速度要快很多,每个 CPU 和内存在一起,称为一个 NUMA 节点
  • NUMA 中的每个节点 Node 用一个 pglist_data 的结构体表示
1
2
3
4
5
6
7
8
9
10
11
12
#define __pfn_to_page(pfn)			\
({ unsigned long __pfn = (pfn); \
unsigned long __nid = arch_pfn_to_nid(__pfn); \
NODE_DATA(__nid)->node_mem_map + arch_local_page_offset(__pfn, __nid);\
})

#define __page_to_pfn(pg) \
({ const struct page *__pg = (pg); \
struct pglist_data *__pgdat = NODE_DATA(page_to_nid(__pg)); \
(unsigned long)(__pg - __pgdat->node_mem_map) + \
__pgdat->node_start_pfn; \
})

图示:

  • 由于每个 CPU 都有自己的本地内存,导致了物理内存必然有一些空洞

其特点如下:

  • 多个内存节点不连续并且存在空隙
  • 适用于 UMA 系统和 NUMA 系统
  • 通过 CONFIG_CONTIGMEM 配置

随着 sparse memory model 的提出,这种内存模型也逐渐被弃用了(ARM在2010年已经移除了对 discontiguous memory model 的支持)

Sparse memory model(稀疏内存模型)

sparse memory model 是为了解决 discontiguous memory model 存在的弊端,而被提出的

  • 连续的地址空间按照 section 被分成一段一段的,其中每一个 section 都是 Hotplug 的,因此内存地址空间可以被切分的更细,支持更离散的不连续内存
  • 被管理的物理内存由一个个任意大小的 mem_section 构成,因此整个物理内存可被视为一个 mem_section 数组,每个 mem_section 包含了一个间接指向 page 数组的指针
1
2
3
4
5
6
7
8
9
10
11
#define __page_to_pfn(pg)					\
({ const struct page *__pg = (pg); \
int __sec = page_to_section(__pg); \
(unsigned long)(__pg - __section_mem_map_addr(__nr_to_section(__sec))); \
})

#define __pfn_to_page(pfn) \
({ unsigned long __pfn = (pfn); \
struct mem_section *__sec = __pfn_to_section(__pfn); \
__section_mem_map_addr(__sec) + __pfn; \
})

图示:

其特点如下:

  • 多个内存区域不连续并且存在空隙
  • 以 section 为单位管理 online 和 hot-plug 内存
  • 支持内存热插拔(hot plug memory),但性能稍逊色于 DISCONTIGMEM
  • 在x86或ARM64内存采用该中模型,其性能比 DISCONTIGMEM 更优并且与 FLATMEM 相当
  • 对于ARM64平台默认选择该内存模型
  • 通过 CONFIG_SPARSEMEM 配置

平台内存模型支持

系统架构 FLATMEM DISCONTIGMEM SPARSEMEM
ARM 默认 不支持 某些系统可选配置
ARM64 不支持 不支持 默认
x86_32 默认 不支持 可配置
x86_32(NUMA) 不支持 默认 可配置
x86_64 不支持 不支持 默认
x86_64(NUMA) 不支持 不支持 默认

参考:linux内存模型)