物理内存的组织方式:每个页面都有一个结构,每一个都有节点

物理内存的组织方式前面咱们讲虚拟内存,涉及物理内存的映射的时候,我们总是把内存想象成它是由连续的一页一页地块组成的 。我们可以从 0 开始对物理页编号,这样每个物理页都会有个页号 。
由于物理地址是连续的,页也是连续的,每个页大小也是一样的 。因而对于任何一个地址,只要直接除一下每页的大小,很容易直接算出在哪一页 。每个页面有一个结构 struct page 表示,这个结构也是放在一个数组里面,这样根据页号,很容易通过下标找到相应的 struct page 结构 。
如果是这样,整个物理内存的布局就非常简单、易管理,这就是最经典的平坦内存模型(Flat Memory Model) 。
我们讲 x86 的工作模式的时候,讲过 CPU 是通过总线去访问内存的,这就是最经典的内存使用方式 。

物理内存的组织方式:每个页面都有一个结构,每一个都有节点

文章插图
 
在这种模式下,CPU 也会有多个,在总线的一侧 。所有的内存条组成一大片内存,在总线的另一侧,所有的 CPU 访问内存都要过总线,而且距离都是一样的,这种模式称为SMP(Symmetric multiprocessing),即对称多处理器 。当然,它也有一个显著的缺点,就是总线会成为瓶颈,因为数据都要走它 。
物理内存的组织方式:每个页面都有一个结构,每一个都有节点

文章插图
 
为了提高性能和可扩展性,后来有了一种更高级的模式,NUMA(Non-uniform memory access),非一致内存访问 。在这种模式下,内存不是一整块 。每个 CPU 都有自己的本地内存,CPU 访问本地内存不用过总线,因而速度要快很多,每个 CPU 和内存在一起,成为一个 NUMA 节点 。但是,在本地内存不足的情况下,每个 CPU 都可以去另外的 NUMA 节点申请内存,这个时候访问延时就会比较长 。
这样,内存被分成了多个节点,每个节点再被分成一个一个的页面 。由于页需要全局唯一定位,页还是需要有全局唯一的页号的 。但是由于物理内存不是连起来的了,页号也就不再连续了 。于是内存模型就变成了非连续内存模型,管理起来就复杂一些 。
这里需要指出的是,NUMA 往往是非连续内存模型 。而非连续内存模型不一定就是 NUMA,有时候一大片内存的情况下,也会有物理内存地址不连续的情况 。
后来内存技术牛了,可以支持热插拔了 。这个时候,不连续成为常态,于是就有了稀疏的内存模型 。
更多linux内核视频教程文档资料免费领取后台私信【内核】自行获取 。
物理内存的组织方式:每个页面都有一个结构,每一个都有节点

文章插图
 
内核学习网站:
Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂
NUMA节点我们主要解析当前的主流场景,NUMA 方式 。我们首先要能够表示 NUMA 节点的概念,于是有了下面这个结构 typedef struct pglist_data pg_data_t,它里面有以下的成员变量:
  • 每一个节点都有自己的 ID:node_id;
  • node_mem_map 就是这个节点的 struct page 数组,用于描述这个节点里面的所有的页;
  • node_start_pfn 是这个节点的起始页号;
  • node_spanned_pages 是这个节点中包含不连续的物理内存地址的页面数;
  • node_present_pages 是真正可用的物理页面的数目 。
例如,64M 物理内存隔着一个 4M 的空洞,然后是另外的 64M 物理内存 。这样换算成页面数目就是,16K 个页面隔着 1K 打开页面,然后是另外 16K 个页面 。这种情况下,node_spanned_pages 就是 33K 个页面,node_present_pages 就是 32K 个页面 。
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; unsigned long node_start_pfn; unsigned long node_present_pages; /* total number of physical pages */ unsigned long node_spanned_pages; /* total size of physical page range, including holes */ int node_id;......} pg_data_t;每一个节点分成一个个区域 zone,放在数组 node_zones 里面 。这个数组的大小为 MAX_NR_ZONES 。我们来看区域的定义 。
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(Direct Memory Access,直接内存存取)的内存 。DMA 是这样一种机制:要把外设的数据读入内存或把内存的数据传送到外设,原来都要通过 CPU 控制完成,但是这会占用空间 CPU,影响 CPU 处理其他事情,所以有了 DMA 模式 。CPU 只需向 DMA 控制器下达指令,让 DMA 控制器来处理数据的传送,数据传送完毕后再把信息反馈给 CPU,这样就可以解放 CPU 。


推荐阅读