一、(N)UMA 模型中的内存组织
Linux支持的各种不同体系结构在内存管理方面差别很大。由于内核的明智设计,以及某些情况下插入的兼容层,这些差别被隐藏起来了。按照先前讨论过的,一个主要的问题是页表中不同数目的间接层。另一个关键是NUMA和UMA系统的划分。
内核对一致和非一致内存访问系统使用相同的数据结构,针对各种不同形式的内存布局,各个算法几乎没有什么差别。在UMA系统上,只使用一个NUMA结点来管理整个系统内存。而内存管理的其他部分则相信它们是在处理一个伪NUMA系统。
1、概述
在讲解内核中用于组织内存的数据结构之前,需要先定义几个概念。首先考虑NUMA系统。图3-3给出了下述内存划分的图示。
首先,内存划分为结点。每个结点关联到系统中的一个处理器,在内核中表示为pg_data_t的实例。
各个结点又划分为内存域,是内存的进一步细分。例如,对可用于 DMA操作的内存区是有限制的。只有前16 MB适用,还有一个高端内存区域无法直接映射。在二者之间是通用的普通内存区。因此一个结点最多由3个内存域组成。内核引入了下列常量来区分它们。
内核引入了下列常量来枚举系统中的所有内存域:
linux/mmzone.h
ZONE_DMA标记适合DMA的内存域。该区域的长度依赖于处理器类型。在IA-32计算机上,一般的限制是16 MB,这是由ISA(工业标准体系结构)设备强加的边界。但更现代的计算机也可能受这一限制的影响。
ZONE_DMA32标记使用32位地址按字寻址、适合DMA的内存域。在32位计算机上,本内存域是空的,即长度为0 MB。
ZONE_NORMAL标记可直接映射到内核段的普通内存域。这是在所有体系结构上保证都会存在的唯一内存域,但无法保证该地址范围对应了实际的物理内存。
ZONE_HIGHMEM标记超出内核段的物理内存。
根据编译时的配置,可能无需考虑某些内存域。例如在64位系统中,并不需要高端内存域。如果支持只能访问4 GB以下内存的32位外设,才需要DMA32内存域。
内核定义一个伪内存域ZONE_MOVABLE,在防止物理内存碎片的机制中需要使用该内存域。
MAX_NR_ZONES充当结束标记,在内核想要迭代系统中的所有内存域时,会用到该常量。
各个内存域都关联一个数组,用来组织属于该内存域的物理内存页(内核中称为页帧)。对每个页帧,都分配一个struct page实例以及所需的管理数据。
各个内存结点保存在一个单链表中,供内核遍历。
出于性能考虑,在为进程分配内存时,内核总是试图在当前运行的CPU相关联的NUMA结点上进行。但这并不总是可行的,例如,该结点的内存可能已经用尽。对此类情况, 每个结点都提供了一个备用列表(借助于struct zonelist)。该列表包含了其他结点(和相关的内存域),可用于代替当前结点分配内存。列表项的位置越靠后,就越不适合分配。
2、数据结构
已经解释用于内存管理的各种数据结构之间的关系,现在分别讲解各个数据结构。
1)结点管理
pg_data_t是用于表示结点的基本元素,定义如下:
linux/mmzone.h
node_zones是一个数组,包含结点中各内存域的数据结构。
node_zonelists指定备用结点及其内存域的列表,以便在当前结点没有可用空间时,在备用结点分配内存。
结点中不同内存域的数目保存在nr_zones。
node_mem_map用于描述结点的所有物理内存页。它包含结点中所有内存域的页。
在系统启动期间,内存管理子系统初始化之前,内核也需要使用内存(另外,还必须保留部分内存用于初始化内存管理子系统)。为解决这个问题,内核使用自举内存分配器( boot memory allocator)。bdata指向自举内存分配器数据结构的实例。
node_start_pfn是该NUMA结点第一个页帧的逻辑编号。系统中所有结点的页帧是依次编号的,每个页帧的号码都是全局唯一的。
node_start_pfn在UMA系统中总是0,因为其中只有一个结点,因此其第一个页帧编号总是0。
node_present_pages结点中页帧的数目,node_spanned_pages该结点以页帧为单位计算的长度。二者的值不一定相同,因为结点中可能有一些空洞,并不对应真正的页帧。
node_id是全局结点ID。系统中的NUMA结点都从0开始编号。
kswapd_wait是交换守护进程( swap daemon)的等待队列,在将页帧换出结点时会用到。kswapd指向负责该结点的交换守护进程的task_struct。kswapd_max_order用于页交换子系统的实现,用来定义需要释放的区域的长度。
图3-3给出结点及其包含的内存域之间的关联,以及备用列表,这些是通过结点数据结构起始处的几个数组建立的。数组的数据保存在结点数据结构之中。
结点的内存域保存在node_zones。该数组总是有3个项,即使结点没有那么多内存域,也是如此。如果不足3个,则其余的数组项用0填充。
结点状态管理
如果系统中结点多于一个,内核会维护一个位图,用以提供各个结点的状态信息。状态是用位掩码指定的,可使用下列值: