《深入Linux内核架构》笔记:(N)UMA模型中的内存组织
2011-01-26 22:05
246 查看
1.概述
在Linux中,内核对一致和非一致内存访问系统使用相同的数据结构,因此针对各种不同形式的内存布局,各个算法几乎没有什么差别。在UMA系统上,只使用一个NUMA结点来管理整个系统内存。
首先内在划分为结点。每个结点关联到系统中的一个处理器,在内核中表示为pg_data_t的结构。
各个结点又划分为内存域,是内存的进一步细分。例如,对可用于(ISA设备的)DMA操作的内存区是有限制的。只有前16M适用,还有一个高端内存区域无法直接映射。在二者之间是通用的“普通”内存区。因此一个结点最多有3个内存域组成,如下图
内核引入了下列常量来枚举系统中的所有内存域:
◆ ZONE_DMA标记适合DMA的内存域。该区域的长度依赖于处理器类型。
◆ ZONE_DMA32标记了使用32位地址字可寻址、适合DMA的内存域。显然只有在64位系统上两种DMA内存域才有差别。
◆ ZONE_NORMAL标记了可直接映射到内核段的普通内存域。这是在所有体系结构上保证都会存在的唯一内存域,但无法保证该地址范围对应了实际的物理内存。
◆ ZONE_HIGHMEM标记了走出内核段的物理内存。
此外内核定义了一个伪内存域ZONE_MOVABLE,在防止物理内存碎片的机制中需要使用该内存域。
MAX_NR_ZONES充当结束标记,在内核想要迭代系统中的所有内存域时,会用到该常量。
各个内在域都关联了一个数组,用来组织属于该内存域的物理内存页(内核中称为页帧)。对每个页帧,都分配了一个struct page的结构以及所需的管理数据。
出于性能考虑,在为进程分配内存时,内核总是试图在当前运行的CPU相关系的NUMA结点上进行。但这并不总是可行的,例如,该结点的内存可能已经用尽。对此类情况,每个结点都提供了一个备用列表。该列表包含了其他结点,可用于代替当前结点分配内存。列表项的位置越靠后,就越不适合分配。
2.数据结构
●结点管理
pg_data_t是用于表示结点的基本元素,定义如下:
◆ node_zones 是一个数组,包含了结点中各个内存域的数据结构
◆ node_zonelists 指定了备用结点及其内存域的列表,以便在当前结点没有可用窨时,在备用结点分配内存。
◆ 结点不同内在域的数目保存在nr_zones中。
◆ node_mem_map是指向page实例数组的指针,用于描述结点的所有物理内存页。它包含了结点中所有内存域的页。
◆ 在系统启动期间,内在管理子系统初始化之前,内核使用了自举内存分配器。bdata指向自举内存分配器数据结构的实例。
◆ node_start_pfn 是该NUMA结点第一个页帧的逻辑编号。系统中所有结点的页帧是依次编号的,每个页帧的号码都是全局唯一的(不只是结点内唯一)。
node_start_pfn在UMA系统中总是0,因为其中只有一个结点,因此其第一个页帧(物理内存页)编号总是0。
◆ node_present_pages 指定了结点中页帧(物理内存页)的数目。
◆ node_spanned_pages 则给出了该结点以页帧(物理内存页)为单位计算的长度。
node_present_pages 与 node_spanned_pages两者的值不一定相同,因为结点中可能有一些空洞,并不对应真正的页帧(物理内存页)
◆ node_id是全局结点的ID。系统中的NUMA结点都是从0开始编号。
◆ pgdat_next 连接到下一个内存点,系统中所有结点都通过单链表连接起来,其末尾通过空指针标记。
◆ kswapd_wait 是交换守护进程的等待队列,在将页帧换出结点时会用到。
结点的内存域保存在node_zones[MAX_NR_ZONES]。该数组总是有3个项,即使结点没有那么多内存域,也是如此。如果不足3个,则其余的数组项用0填充。
●结点状态管理
如果系统中结点多于一个,内核会维护一个位图,用以提供各个结点状态信息。状态是用位掩码指定的,可使用下列值:
状态N_POSSIBLE、N_ONLINE和N_CPU用于CPU和内存的执插拔,在这里不考虑这些特性。
内存管理有必要的标志是N_HIGH_MEMORY 和 N_NORMAL_MEMORY。如果结点有普通或高端内存则使用 N_HIGH_MEMORY,仅当结点没有高端内存才设置N_NORMAL_MEMORY。
两个辅助函数来设置或清除位域或特定结点中的一个比特位:
<nodemask.h>
void node_set_state( int node , enum node_states state);
void node_clear_state( int node , enum node_states state );
此外宏for_each_node_state(__node,__state)用来迭代处于特定状态的所有结点,
而for_each_online_node(node) 则迭代所有活动结点。
如果内核编译为只支持单个结点(即使用平坦内在模型),则没有结点位图,上术操作该位图的函数则变为空操作。
在Linux中,内核对一致和非一致内存访问系统使用相同的数据结构,因此针对各种不同形式的内存布局,各个算法几乎没有什么差别。在UMA系统上,只使用一个NUMA结点来管理整个系统内存。
首先内在划分为结点。每个结点关联到系统中的一个处理器,在内核中表示为pg_data_t的结构。
各个结点又划分为内存域,是内存的进一步细分。例如,对可用于(ISA设备的)DMA操作的内存区是有限制的。只有前16M适用,还有一个高端内存区域无法直接映射。在二者之间是通用的“普通”内存区。因此一个结点最多有3个内存域组成,如下图
内核引入了下列常量来枚举系统中的所有内存域:
<mmzone.h> 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, MAX_NR_ZONES };
◆ ZONE_DMA标记适合DMA的内存域。该区域的长度依赖于处理器类型。
◆ ZONE_DMA32标记了使用32位地址字可寻址、适合DMA的内存域。显然只有在64位系统上两种DMA内存域才有差别。
◆ ZONE_NORMAL标记了可直接映射到内核段的普通内存域。这是在所有体系结构上保证都会存在的唯一内存域,但无法保证该地址范围对应了实际的物理内存。
◆ ZONE_HIGHMEM标记了走出内核段的物理内存。
此外内核定义了一个伪内存域ZONE_MOVABLE,在防止物理内存碎片的机制中需要使用该内存域。
MAX_NR_ZONES充当结束标记,在内核想要迭代系统中的所有内存域时,会用到该常量。
各个内在域都关联了一个数组,用来组织属于该内存域的物理内存页(内核中称为页帧)。对每个页帧,都分配了一个struct page的结构以及所需的管理数据。
出于性能考虑,在为进程分配内存时,内核总是试图在当前运行的CPU相关系的NUMA结点上进行。但这并不总是可行的,例如,该结点的内存可能已经用尽。对此类情况,每个结点都提供了一个备用列表。该列表包含了其他结点,可用于代替当前结点分配内存。列表项的位置越靠后,就越不适合分配。
2.数据结构
●结点管理
pg_data_t是用于表示结点的基本元素,定义如下:
]<mmzone.h> typedef struct pglist_data { struct zone node_zones[MAX_NR_ZONES]; struct zonelist node_zonelists[MAX_ZONELISTS]; int nr_zones; struct page *node_mem_map; struct bootmem_data *bdata; unsigned long node_start_pfn; // 物理内存页总数 unsigned long node_present_pages; // 物理内存页的总长度,包含洞在内 unsigned long node_spanned_pages; int node_id; struct pglist_data *pgdat_next; wait_queue_head_t kswapd_wait; struct task_struct *kswapd; int kswapd_max_order; } pg_data_t;
◆ node_zones 是一个数组,包含了结点中各个内存域的数据结构
◆ node_zonelists 指定了备用结点及其内存域的列表,以便在当前结点没有可用窨时,在备用结点分配内存。
◆ 结点不同内在域的数目保存在nr_zones中。
◆ node_mem_map是指向page实例数组的指针,用于描述结点的所有物理内存页。它包含了结点中所有内存域的页。
◆ 在系统启动期间,内在管理子系统初始化之前,内核使用了自举内存分配器。bdata指向自举内存分配器数据结构的实例。
◆ node_start_pfn 是该NUMA结点第一个页帧的逻辑编号。系统中所有结点的页帧是依次编号的,每个页帧的号码都是全局唯一的(不只是结点内唯一)。
node_start_pfn在UMA系统中总是0,因为其中只有一个结点,因此其第一个页帧(物理内存页)编号总是0。
◆ node_present_pages 指定了结点中页帧(物理内存页)的数目。
◆ node_spanned_pages 则给出了该结点以页帧(物理内存页)为单位计算的长度。
node_present_pages 与 node_spanned_pages两者的值不一定相同,因为结点中可能有一些空洞,并不对应真正的页帧(物理内存页)
◆ node_id是全局结点的ID。系统中的NUMA结点都是从0开始编号。
◆ pgdat_next 连接到下一个内存点,系统中所有结点都通过单链表连接起来,其末尾通过空指针标记。
◆ kswapd_wait 是交换守护进程的等待队列,在将页帧换出结点时会用到。
结点的内存域保存在node_zones[MAX_NR_ZONES]。该数组总是有3个项,即使结点没有那么多内存域,也是如此。如果不足3个,则其余的数组项用0填充。
●结点状态管理
如果系统中结点多于一个,内核会维护一个位图,用以提供各个结点状态信息。状态是用位掩码指定的,可使用下列值:
<nodemask.h> enum node_states { // 结点在某个时候可能变为联机 N_POSSIBLE, // 结点是联机的 N_ONLINE, // 结点是普通内存域 N_NORMAL_MEMORY, // 结点是普通或高端内存域 #ifdef CONFIG_HIGHMEM N_HIGH_MEMORY, #else N_HIGH_MEMORY = N_NORMAL_MEMORY, #endif // 结点有一个或多个CPU N_CPU, NR_NODE_STATES };
状态N_POSSIBLE、N_ONLINE和N_CPU用于CPU和内存的执插拔,在这里不考虑这些特性。
内存管理有必要的标志是N_HIGH_MEMORY 和 N_NORMAL_MEMORY。如果结点有普通或高端内存则使用 N_HIGH_MEMORY,仅当结点没有高端内存才设置N_NORMAL_MEMORY。
两个辅助函数来设置或清除位域或特定结点中的一个比特位:
<nodemask.h>
void node_set_state( int node , enum node_states state);
void node_clear_state( int node , enum node_states state );
此外宏for_each_node_state(__node,__state)用来迭代处于特定状态的所有结点,
而for_each_online_node(node) 则迭代所有活动结点。
如果内核编译为只支持单个结点(即使用平坦内在模型),则没有结点位图,上术操作该位图的函数则变为空操作。
相关文章推荐
- (N)UMA 模型中的内存组织------《深入Linux内核架构》笔记
- N)UMA 模型中的内存组织------《深入Linux内核架构》笔记
- 3.2 (N)UMA模型的内存组织
- Java 内存模型笔记
- 【C++】《C++ Primer Plus》笔记(6)——内存模型和命名空间
- C++学习笔记(第9章->内存模型和名称空间)
- 04_ARM汇编自学笔记模型之ARM 状态下的寄存器组织
- 《C++ Primer Plus》第9章 内存模型和名称空间 学习笔记
- 内存模型笔记
- JVM 学习笔记(一) JVM内存模型
- c++学习笔记 内存四区 函数调用模型 指针强化
- 【Java学习笔记】JVM内存模型
- Java多线程笔记一(创建运行,相关概念,JVM内存模型,线程有几种状态,死锁)
- 1.C++学习笔记:内存模型
- iOS小明开发笔记(二十二) (C语言之内存四区和函数调用模型)
- 《C++ Primer Plus(第六版)》(13)(第九章 内存模型和命名空间 笔记)
- C++对象模型笔记:对象的三种内存布局
- java 内存模型笔记(一)
- JVM笔记(一)技术体系和内存区域模型
- C++对象模型笔记:对象的三种内存布局