Linux将物理内存按固定大小的页面(一般为4K)划分内存,在内核初始化时,会建立一个全局struct page结构数组mem_map[ ]。如系统中有76G物理内存,则物理内存页面数为76*1024*1024k/4K= 19922944个页面,mem_map[ ]数组大小19922944,即为数组中每个元素和物理内存页面一一对应,整个数组就代表着系统中的全部物理页面。 在服务器中,存在NUMA架构(如Nehalem、Romly等),Linux将NUMA中内存访问速度一致(如按照内存通道划分)的部分称为一个节点(Node),用struct pglist_data数据结构表示,通常使用时用它的typedef定义pg_data_t。系统中的每个结点都通过pgdat_list链表pg_data_t->node_next连接起来,该链接以NULL为结束标志。每个结点又进一步分为许多块,称为区域(zones)。区域表示内存中的一块范围。区域用struct zone_struct数据结构表示,它的typedef定义为zone_t。

每个区域(Zone)中有多个页面(Pages)组成。节点、区域、页面三者关系如下图。

1 节点(Node)

节点(Node),在linux中用struct pglist_data数据结构表示,通常使用时用它的typedef定义pg_data_t,数据结构定义在文件include/linux/mmzone.h中。当分配一个页面时,linux使用本地结点分配策略,从运行的CPU最近的一个结点分配。 因为进程倾向于在同一个CPU上运行,使用内存时也就更可能使用本结点的空间。对于象PC之类的UMA系统,仅有一个静态的pg_data_t结构,变量名为contig_page_data。遍历所有节点可以使用for_each_online_pgdat(pgdat)来实现。

2 区域(Zone)

节点(Node)下面可以有多个区域, 共有以下几种类型:

1)ZONE_DMA
它是低内存的一块区域,这块区域由标准工业架构(Industry Standard Architecture)设备使用,适合DMA内存。这部分区域大小和CPU架构有关,在x86架构中,该部分区域大小 限制为16MB。

2)ZONE_DMA32
该部分区域为适合支持32位地址总线的DMA内存空间。很显然,该部分仅在64位系统有效,在32位系统中,这部分区域为空。在x86-64架构中,这部分的区域范围为0~4GB。

3)ZONE_NORMAL
属于ZONE_NORMAL的内存被内核直接映射到线性地址。这部分区域仅表示可能存在这部分区域,如在64位系统中,若系统只有4GB物理内存,则所有的物理内存都属于 ZONE_DMA32,而ZONE_NORMAL区域为空。许多内核操作都仅在ZONE_NORMAL内存区域进行,所以这部分是系统性能关键的地 方。

4)ZONE_HIGHMEM
是系统中剩下的可用内存,但因为内核的地址空间有限,这部分内存不直接映射到内核。

在x86架构中内存有三种区域:ZONE_DMA,ONE_NORMAL,ZONE_HIGHMEM,不同类型的区域适合不同需要。在32位系统中结构中,1G(内核空间)/3G(用户空间) 地址空间划分时,三种类型的区域如下:
ZONE_DMA              内存开始的16MB
ZONE_NORMAL       16MB~896MB
ZONE_HIGHMEM     896MB ~ 结束

4G(内核空间)/4G(用户空间)地址空间划分时,三种类型区域划分为:
ZONE_DMA              内存开始的16MB
ZONE_NORMAL       16MB~3968MB
ZONE_HIGHMEM     3968MB ~ 结束

在64位Linux系统中,内存只有三个区域DMA、DMA32和NORMAL。
ZONE_DMA              内存开始的16MB
ZONE_DMA32          16MB~4GB
ZONE_NORMAL       4GB ~ 结束

下图是32位系统和64位系统对应的内存区域划分:

3 页面(Page)

系统内存由固定的块组成,称为页帧,每个页帧由struct page结构描述。内核在初始化时,会根据内存的大小计算出由多少页帧,每个页帧都会有一个page结构与之对应,这些 信息保存在全局数组变量mem_map中。mem_map通常存储在ZONE_NORMAL区域中, 在内存较小的机器中,会保存在加载内核镜像后的一片保留空间里。有多少个物理页面,就会有多个struct page结构,如系统安装128GB物理内存,struct page结构体大小为40字节,则mem_map[ ]数组就占用物理内存大小为128*1024*1024k/4k * 40 = 1280MB,即Linux内核要使用1280MB物理内存来保存mem_map[ ]数组,这部分内存是不可被使用的,因此 struct page结构体大小不能设计很大。

页面标志尤为重要,在内存分配与回收、I/O操作等重要内核活动过程中都会使用到页面标志。所有的标志在include/linux/page-flags.h中定义。下面解释一下重要的几个页面标志:
PG_locked:页面是否被锁住,若该位设置了该位,则不允许内核其他部分访问该页面。这用来防止内存管理过程中遇到的竞争条件,如当从硬盘读取数据到一个页面时,就不允许其他内核部分访问该页面,因为读数据的过程中,其他内核部分能访问的话,则读取到的数据是不完整的。
PG_error:I/O出错,且操作和页面有关,就设置该标志。
PG_referenced和PG_active:控制系统使用页面的活跃程度,这个信息对swap系统选择待交换出的页面非常重要。
PG_update:表示成功完成从块设备上读取一个页面的数据。该标志和块设备I/O操作有关。
PG_dirty:当内存页面中的数据和块设备上的数据不一致时,就设置该标志。在写数据到块设备时,为了提高将来的读性能,数据并不是立即回写到块设备上,而只是设置页面脏 标志,表示该页面数据需要回写。
PG_lru:该标志用来实现页面回收和交换。
PG_highmem:表示该页面为属于高端内存。

Logo

更多推荐