四、物理地址空间设计模型

Linux内存管理 | 四、物理地址空间设计模型 #

前面几篇文章,主要讲解了虚拟内存空间的布局和管理,下面同步来聊聊物理内存空间的布局和管理。

 

1、物理内存 #

什么是物理内存?

我们平时聊的内存,也叫随机访问存储器(random-access memory),也叫RAM

RAM分为两类:

  • SRAM:静态RAM,其主要用于CPU高速缓存 L1CacheL2CacheL3Cache,其特点是访问速度快,访问速度为 1 - 30 个时钟周期,但是容量小,造价高。

CPU缓存结构.png

  • DRAM:动态RAM,其主要用于我们常说的主存上,其特点的是访问速度慢(相对高速缓存),访问速度为 50 - 200 个时钟周期,但是容量大,造价便宜些(相对高速缓存)。

image.png

DRAM经过组合起来,就作为我们的计算机内存,也是物理内存。

 

2、物理内存访问模型 #

上面介绍了物理内存的基本组成,那么CPU是如何访问物理内存的呢?

对于CPU访问物理内存,Linux提供了两种架构:UMA(Uniform Memory Access)一致内存访问,NUMA(Non-Uniform Memory Access)非一致内存访问。

2.1 UMA #

UMA架构下,多核处理器中的多个CPU,位于总线的一侧,所有的内存条组成的物理内存位于总线的另一侧。

所有的CPU访问内存都要经过总线,并且距离都是一样的,所以在UMA架构下,所有CPU具有相同的访问特性,即对内存的访问具有相同的速度。

image-20231013075142500

2.2 NUMA #

这种架构,系统中的各个处理器都有本地内存,处理器与处理器之间也通过总线连接,以便于其他处理器对本地内存的访问。

UMA不同的是,处理器访问本地内存的速度要快于对其他处理器本地内存的访问。

image-20231013074823586

3、物理内存组织模型 #

内存页是物理内存管理中最小单位,有时也成为页帧(Page Frame)

内核对物理内存划分为一页一页的连续的内存块,每页大小4KB,并且使用struct page结构体来表示页结构,其中封装了每个页的状态信息,包括:组织结构,使用信息,统计信息等。

page结构体较为复杂,我们后续再深入了解。

 

更多干货可见:高级工程师聚集地,助力大家更上一层楼!

 

3.1 FLATMEM平坦内存模型 #

FLATMEM即:flat memory model

我们把物理内存想象成它是由连续的一页一页的块组成的,我们从0开始对物理页编号,这样每个物理页都会有页号。

由于物理地址是连续的,页也是连续的,每个页大小也是一样的。因而对于任何一个地址,只要直接除一下每页的大小,很容易直接算出在哪一页。

如果是这样,整个物理内存的布局就非常简单、易管理,这就是最经典的平坦内存模型(Flat Memory Model)

image.png

如上图,平坦内存模型中,内核使用一个mem_map的全局数组,来组织所有划分出来的物理内存页,下标由PFN表示。

在平坦内存模型下 ,page_to_pfnpfn_to_page 的计算逻辑就非常简单,本质就是基于 mem_map 数组进行偏移操作。

#ifndef ARCH_PFN_OFFSET
#define ARCH_PFN_OFFSET		(0UL)
#endif

#if defined(CONFIG_FLATMEM)
#define __pfn_to_page(pfn) (mem_map + ((pfn)-ARCH_PFN_OFFSET))
#define __page_to_pfn(page) ((unsigned long)((page)-mem_map) + ARCH_PFN_OFFSET)
#endif

ARCH_PFN_OFFSETPFN 的起始偏移量。

 

3.2 DISCONTIGMEM 不连续内存模型 #

DISCONTIGMEM即:discontiguous memory model

我们早期内核使用的是FLATMEM模型,该模型对于较小的,连续的物理空间是方便使用的,但是当物理内存不连续时,使用mem_map管理,就会出现空洞,这会浪费mem_map数组本身占用的内存空间。

image.png

对于NUMA访问内存模型,物理内存分布就是不连续的,为了有效管理,DISCONTIGMEM 不连续内存模型出现了。

image.png

在不连续的物理内存中,DISCONTIGMEM不连续内存模型,将物理内存分成了一个个的node,然后每个node管理一块连续的物理内存,连续的物理内存仍然使用FLATMEM平坦内存模型来管理,从而避免了内存空洞的浪费。

我们可以看出 DISCONTIGMEM 非连续内存模型其实就是 FLATMEM 平坦内存模型的一种扩展。

DISCONTIGMEM是个稍纵即逝的内存模型,在SPARSEMEM出现后即被完全替代

 

3.3 SPARSEMEM稀疏内存模型 #

随着内存技术的发展,内核可以支持物理内存的热插拔了(像我们的内存条,可以直接插入拔出),这样不连续物理内存已然称为常态。

SPARSEMEM稀疏内存模型的核心思想就是对粒度更小的连续内存块进行精细的管理,用于管理连续内存块的单元被称作 section

物理页大小为 4k 的情况下, section 的大小为 128M ,物理页大小为 16k 的情况下, section 的大小为 512M

 

在内核中,使用struct mem_section结构体表示SPARSEMEM模型中的section

struct mem_section {
	unsigned long section_mem_map;
        ...
}
  • 每个mem_section管理一片小的,物理内存连续的区域,并且支持对该区域的offline/online状态

  • 所有的mem_section都保存在一个全局数组中

整体的框架如下:

image.png

SPARSEMEM 稀疏内存模型下 page_to_pfnpfn_to_page 的计算逻辑又发生了变化。

#if defined(CONFIG_SPARSEMEM)
/*
 * Note: section's mem_map is encoded to reflect its start_pfn.
 * section[i].section_mem_map == mem_map's address - start_pfn;
 */
#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;		\
})
#endif
  • page_to_pfn 的转换中,首先需要通过 page_to_section 根据 struct page 结构定位到 mem_section 数组中具体的 section 结构。然后在通过 section_mem_map 定位到具体的 PFN
  • pfn_to_page 的转换中,首先需要通过 __pfn_to_section 根据 PFN 定位到 mem_section 数组中具体的 section 结构。然后在通过 PFNsection_mem_map 数组中定位到具体的物理页 Page

 

4、总结 #

以上,我们先对物理内存空间有一个基础的了解,明白物理内存空间的内存访问模型和组织模型,下面我们再详细介绍物理内存空间的布局和管理。

欢迎关注【嵌入式艺术】,董哥原创!
img