物理内存与硬件内存组织 - linux内存管理(三)

上一章我们梳理了Node, Zone, Page Frame的整个流程,本章就来整理其关系和数据结构之间的关系。

1. 基本概念
NUMA(Non-Uniform Memory Access,非统一内存访问)和UMA(Uniform Memory Access,统一内存访问):

NUMA是从处理器对内存访问速度不同的结构
UMA是处理器与所有内存的访问速度相同的结构
结点Node:

从1个CPU访问速度相同的内存集合
每个CPU对应一个本地物理内存
在内核中用pg_data_t类型,表示节点的结构体成为节点描述符
ZONE:

节点中具有相同属性的区域
内核使用struct zone结构体管理
页帧Page:

ZONE中管理物理内存的最小单位称为页帧
页帧在Linux中由page结构体管理,通过mem_map全局数据访问
Linux采用Node、Zone和页三级结构来描述物理内存的,如图所示

2. 结点
Linux采用一个struct pg_data_t结构体来描述系统的内存,每一个Node都对应一个struct pglist_data,系统中每个节点都挂接在一个pgdat_list列表中,对于NUMA系统中一个,使用的全局变量struct pglist_data __refdata contig_page_data。

下面就对结构体提的主要域进行说明

结构体成员变量    说明
node_zones    该结点的zone类型,一般包括ZONE_HIGHMEM、ZONE_NORMAL和ZONE_DMA三类,包含了节点中各个内存域的数据结构
node_zonelists    指定了备用节点及其内存域列表,以便在当前结点没有可用空间时,在备用结点分配内存
nr_zones    该结点的 zone 个数,可以从 1 到 3,但并不是所有的结点都需要有 3 个 zone
node_mem_map    它是 struct page 数组的第一页,该数组表示结点中的每个物理页框。根据该结点在系统中的顺序,它可在全局 mem_map 数组中的某个位置
node_start_pfn    当前NUMA节点第一页帧逻辑编号。在UMA总是0.
node_present_pages    总共可用的页面数
node_spanned_pages    总共的页面数,包括有空洞的区域
kswapd    页面回收进程
3. ZONE
每个结点的内存被分为多个块,称为zones,它表示内存中一段区域。一个zone用struct_zone_t结构描述,zone的类型主要有ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM。ZONE_DMA位于低端的内存空间,用于某些旧的ISA设备,ISA总线的直接内存存储DMA,只能对RAM的前16MB进行寻址。ZONE_NORMAL的内存直接映射到Linux内核线性地址空间的高端部分,许多内核操作只能在ZONE_NORMAL中进行。因此对于内核来说, 不同范围的物理内存采用不同的管理方式和映射方式,Linux使用enum zone_type来标记内核所支持的所有内存区域

enum zone_type
{
#ifdef CONFIG_ZONE_DMA
    ZONE_DMA,
#endif
#ifdef 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的内存域.该区域的长度依赖于处理器类型.这是由于古老的ISA设备强加的边界. 但是为了兼容性,现代的计算机也可能受此影响
ZONE_DMA32    标记了使用32位地址字可寻址,适合DMA的内存域,在32位系统中,本区域是空的, 即长度为0MB,在Alpha和AMD64系统上,该内存的长度可能是从0到4GB
ZONE_NORMAL    标记了可直接映射到内存段的普通内存域.这是在所有体系结构上保证会存在的唯一内存区域
ZONE_HIGHMEM    标记了超出内核虚拟地址空间的物理内存段,因此这段地址不能被内核直接映射
ZONE_MOVABLE    内核定义了一个伪内存域ZONE_MOVABLE,在防止物理内存碎片的机制memory migration中需要使用该内存域.供防止物理内存碎片的极致使用
ZONE_DEVIC    为支持热插拔设备而分配的Non Volatile Memory非易失性内存
Zone是用struct zone_t描述的,它跟踪页框使用、空闲区域和锁等信息,结构体中主要域说明如下

结构体成员变量    说明
watermark    水位值,WMARK_MIN/WMARK_LOV/WMARK_HIGH,页面分配器和kswapd页面回收中会用到
lowmem_reserve    zone中预留的内存,用于一些无论如何都不能失败的关键性内存分配
zone_pgdat    执行所属的pglist_data
pageset    Per-CPU上的页面,减少自旋锁的争用
zone_start_pfn    ZONE的起始内存页面帧号
managed_pages    被Buddy System管理的页面数量
spanned_pages    ZONE中总共的页面数,包含空洞的区域
present_pages    ONE里实际管理的页面数量
struct free_area free_area[MAX_ORDER];    管理空闲页面的列表
当系统中可用的内存比较少时,kswapd将被唤醒,并进行页交换。如果需要内存的压力非常大,进程将同步释放内存。每个zone有三个阙值,成为pages_low/pages_min/pages_high,用于跟踪该zone的内存压力。

pages_min的页框数是由内存初始化free_area_init_core函数,根据zone内页框的比例计算,最小为20页,最大一般为255页
当到达pages_min时,说明页面数非常紧张,分配页面的动作和kswapd线程同步运行
当空闲也的数目达到pages_low时,说明页面刚开始紧张,则kswapd线程将被唤醒,并开始释放回收页面
当达到pages_high时,说明内存页面数很充足,不需要回收,kswapd线程将重新休眠,通常这个数值是page_min的3倍
4. page
每个物理页框都需要一个对应的page结构来进行管理,记录分配状态,分配和回收,互斥以及同步存在。 因为内核会为每一个物理页帧创建一个struct page的结构体,因此要保证page结构体足够的小,否则仅struct page就要占用大量的内存。出于节省内存的考虑,struct page中使用了大量的联合体union。

5. 总结
本章梳理了Node, Zone, Page Frame各个数据结构的成员变量和关系,对于Linux中管理内存的各个结构体之间的关系图如下图所示

在这里插入图片描述

前面已经分析把物理内存添加到memblock以及给物理内存建立页表映射,这里我们分析内存模型。在Linux内核中支持3种内存模型,分别为

flat memory model
Discontiguous memory model
sparse memory model
所谓memory model,其实就是从cpu的角度看,其物理内存的分布情况,在linux kernel中,使用什么的方式来管理这些物理内存。某些体系架构支持多种内存模型,但在内核编译构建时只能选择使用一种内存模型。

在这里插入图片描述
1. 基本概念
1.1 page frame
从虚拟地址到物理地址的映射过程,系统对于内存管理是以页为单位进行管理的。在linux操作系统中,物理内存是按照page size来管理的,具体page size是多少是和硬件以及linux系统配置相关的,4k是最经典的设定。因此,对于物理内存,我们将其分成一个个按page size排列的page,每一个物理内存中的page size的内存区域我们称之page frame。page frame是系统内存的最小单位,对内存中的每个页都会创建struct page实例。

1.2 PFN
对于一个计算机系统,其整个物理地址空间应该是从0开始,到实际系统能支持的最大物理空间为止的一段地址空间。在ARM系统中,假设物理地址是32个bit,那么其物理地址空间就是4G,在ARM64系统中,如果支持的物理地址bit数目是48个,那么其物理地址空间就是256T。当然,实际上这么大的物理地址空间并不是都用于内存,有些也属于I/O空间(当然,有些cpu arch有自己独立的io address space)。因此,内存所占据的物理地址空间应该是一个有限的区间,不可能覆盖整个物理地址空间。

PFN是page frame number的缩写,所谓page frame,就是针对物理内存而言的,把物理内存分成一个个固定长度为page size的区域,并且给每一个page 编号,这个号码就是PFN。与page frame的转换关系如下图所示

1.3 NUMA
在多核的系统设计中内存的架构有两种类型计算机,分别以不同的方式管理物理内存。

UMA计算机(一致内存访问,uniform memory access):将可用内存以连续方式组织起来,系统中所有的处理器共享一个统一的,一致的物理内存空间,无论从哪个处理器发起访问,对内存的访问时间都是一样快。其架构图如下图所示

NUMA计算机(非一致内存访问,non-uniform memory access):每个 CPU 都有自己的本地内存,CPU 访问本地内存不用过总线,因而速度要快很多,每个 CPU 和内存在一起,称为一个 NUMA 节点。但是,在本地内存不足的情况下,每个 CPU 都可以去另外的 NUMA 节点申请内存,这个时候访问延时就会比较长。

从图中可以看出,每个CPU访问local memory,速度更快,延迟更小。当然,整体的内存构成一个内存池,CPU也能访问remote memory,相对来说速度更慢,延迟更大。目前对NUMA的了解仅限于此,在内核中会遇到相关的代码,大概知道属于什么范畴就可以了。

2. linux内存模型
Linux提供了三种内存模型(include/asm-generic/memory_model.h),一般处理器架构支持一种或者多种内存模型,这个在编译阶段就已经确定,比如目前在ARM64中,使用的Sparse Memory Model。

2.1 FLAT memory model(平坦内存模型)
如果从系统中任意一个CPU的角度来看,当它访问物理内存的时候,物理地址空间是一个连续的,没有空洞的地址空间,那么这种计算机系统的内存模型就是Flat memory。

早期的系统物理内存不大,那个时候Linux使用平坦内存模型(flat memory model)来管理物理内存就足够有效了。一个page frame用一个struct page结构体表示,整个物理内存可以用一个由所有struct page构成的数组mem_map表示,而经过页表查找得到的PFN,正好可以用来做这个数组的小标,__pfn_to_page()函数就是专门来完成这个功能的。

#define __pfn_to_page(pfn)  (mem_map + ((pfn) - ARCH_PFN_OFFSET)) 
1


对于FLATMEM来说,物理内存本身是连续的,如果不连续的话,那么中间一部分物理地址是没有对应的物理内存,就会形成一个个洞,这就浪费了mem_map数组本身占用的内存空间。对于这种模型,其特点如下:

内存连续且不存在空隙
这种在大多数情况下,应用于UMA系统“Uniform Memory Access”。
通过CONFIG_FLATMEM配置
2.2 discontiguous memory model (不连续内存模型)
如果CPU在访问物理内存的时候,其地址空间是有一些空洞的,是不连续的,那么这种计算机系统的内存模型就是Discontiguous memory。在什么情况下物理内存是不连续的呢?当NUMA出现后,为了有效的管理NUMA模式的物理内存,一种被称为不连续内存模型的实现于1999年被引入linux系统中。在这中模型中,NUMA中的每个Node用一个叫做pglist_data的结构体表示。


应对不连续物理内存的问题似乎是解决了,可是现在你给我一个物理page的地址,使用DISCONTIGMEM的话,我怎么知道这个page是属于哪个node的呢,PFN中可没有包含node编号啊。pfn_to_page()之前干的活多轻松啊,就是索引下数组就得到数组元素struct page了,现在PFN和page之间的对应关系不是那么直接了,pfn_to_page的任务就开始重起来了。

#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);\ 
})
1
2
3
4
5
物理内存存在空洞,随着Sparse Memory的提出,这种内存模型也逐渐被弃用了。这种内存模型有以下的特点

多个内存节点不连续并且存在空隙"hole"
适用于UMA系统和NUMA系统
ARM在2010年已经移除了对DISCONTIGMEM的支持
通过CONFIG_CONTIGMEM配置
2.3 sparse memory model(稀疏内存模型)
内存模型是一个逐渐演化的过程,刚开始的时候,由于内存比较小,使用flat memory模型去抽象一个连续的内存地址空间。但是出现了NUMA架构之后,整个不连续的内存空间被分配成若干个node,每个node上是连续的内存地址空间,为了有效的管理NUMA模型下的物理内存,就开始使用discontiguous memory model。为了解决DISCONTIGMEM存在的弊端,一种新的稀疏内存模型被使用出来。

在sparse memory内存模型下,连续的地址空间按照SECTION被分成一段一段的,其中每一个section都是Hotplug的,因此sparse memory下,内存地址空间可以被切分的更细,支持更离散的Discontiguous memory。在SPARSEMEM中,被管理的物理内存由一个个任意大小的section(struct mem_section表示)构成,因此整个物理内存可被视为一个mem_section数组。每个mem_section包含了一个间接指向struct page数组的指针。

其主要的特点如下:

多个内存区域不连续并且存在空隙
支持内存热插拔(hot plug memory),但性能稍逊色于DISCONTIGMEM
在x86或ARM64内存采用该中模型,其性能比DISCONTIGMEM更优并且与FLATMEM相当
对于ARM64平台默认选择该内存模型
以section为单位管理online和hot-plug内存
通过CONFIG_SPARSEMEM配置
section大小从几十MiB到几GiB不等,取决于体系架构和内核的配置。通常在系统配置中将内存扩展单元「memory expansion unit」用作section大小。比如,如果系统内存可扩展至64GiB,并且最小内存扩展单元为1GiB,则设置section大小也为1GiB。当使用Linux系统作为hypervisor的客户操作系统「guest OS」,也是以section大小为单元在运行时向Linux系统增添内存和移除Linux系统的内存。

3. 平台内存模型支持
Linux支持的各种不同体系结构在内存管理方面差别很大,以下是主流的架构支持情况如下表所示,一个体系架构中可能有多种内存模型可用(ARM64只支持一种内存模型),通过可选的内核配置选项来决定使用哪种内存模型。

系统架构    FLATMEM    DISCONTIGMEM    SPARSEMEM
ARM    默认    不支持    某些系统可选配置
ARM64    不支持    不支持    默认
x86_32    默认    不支持    可配置
x86_32(NUMA)    不支持    默认    可配置
x86_64    不支持    不支持    默认
x86_64(NUMA)    不支持    不支持    默认
4.小结
这章我们学习了3种内核模型的各自原理和特点,同时我们简单介绍linux kernel,对于这三种模型使用什么样的方式来管理这些物理内存,后面的章节中会针对FLAT(ARM)和SPARSE(ARM64)模型做相应的介绍。

你可能感兴趣的:(等待删除,内存,linux内核)