3.2.3 物理内存模型
前面提到,系统管理物理内存的基本单位是页(Page),物理内存被划分成一个个大小固定的页帧(Page Frame),每个页帧都有一个唯一的编号,叫着PFN(Page Frame Number); 内核中使用数据结构 page
来封装每个物理页帧的状态,因此物理页帧与 page
之间存在着1:1的关系。系统必须提供两者之间相互转换的方式,即系统可以通过PFN 拿到对应的 page
, 也可以通过 page
得到对应的 PFN, 内核中提供了两个宏来完成该转换,分别是 page_to_pfn
与 pfn_to_page
。如何组织内存页帧,使得这种转换能够高效便捷地实现,是内核设计者需要解决的问题。
我们将组织管理物理页帧的方式叫物理内存模型,内存模型的结构与物理内存本身的结构息息相关,本节我们将简要介绍一下内核物理内存模型的演进过程,以及每个内存模型所适用的场景。
3.2.3.1 FLATMEM
理想情况下,物理内存是一块地址连续的存储空间,这样物理页帧的PFN也是连续的,因此最简单直接地方式是将所有 page
放在一个一维数组中,每个 page
的索引就是对应物理页帧的PFN. 这种内存模型叫着平坦内存模型(Flat Memory Model),Linux早期使用的就是这种内存模型,所有的 page
保存在全局变量 mem_map
中,PFN 与 page
相互转换的逻辑实现也很直接,参考如下代码:
ARCH_PFN_OFFSET
是页帧号的起始偏移量,总体来说两个宏的逻辑都是基于 mem_map
做地址运算,效率非常高。
3.2.3.2 DISCONTIGMEM
FLATMEM很适合用来管理连续的物理内存,但对于内存不连续的情况就不太友好,如果物理内存存在大块的不连续区间,由于 mem_map
使用PFN作为 page
的索引,那么这些不连续区间对应的PFN就也会占用 mem_map
中的位置,形成空洞(hole),造成大量的内存空间浪费。另外就是在NUMA架构下,每个 Node 都有自己单独的内存区域,使用全局的变量来追踪所有的物理内存也不合理。
为了解决该问题,内核提供了新的内存模型叫着 DISCONTIGMEM
, 意在消除内存空洞对 mem_map
的资源浪费。为了简化,内核在实现时仅根据 NUMA 的内存节点进行了划分,依然将每个 node 的内存看着是连续的,FLATMEM 中的全局变量 mem_map
变成了 pglist_data
中的一个变量:
该模型实际上是 FLATMEM 的扩展,PFN与 page
之间的转换比FLATMEM多了一步:通过PFN或者 page
确定所在的node. 其余逻辑就与FLATMEM 一样了。具体代码如下:
3.2.3.3 SPARSEMEM
DISCONTIGMEM 的本意是应对非连续的物理内存,但其又将NUMA架构下的每个 node 看着是连续的,这其实并不合理,特别是在支持内存热插拔的系统当中。系统需要一种机制能够更灵活地管理粒度更小的连续内存区块,这种内存模型叫着 SPARSEMEM
, 即稀疏内存模型。
SPARSEMEM模型的核心思想是将每个连续的内存块都单独管理,每个连续的内存块叫着一个 mem_section
, 该数据结构中的字段 section_mem_map
指向连续的 page
对象,所有的 memsection 存放在一个全局的数组中,并且每个 mem_section
都可以在系统运行时改变 offline/online 状态,以便支持内存的热插拔(hotplug)功能。此时,PFN 与 page 之间的相互转换需要先找到对应的块,然后在通过 section_mem_map
属性进行查找。
很明显SPARSEMEM已经完全覆盖了前两个内存模型的所有功能,特别是可以完全替代不够灵活的DISCONTIGMEM.
3.2.3.4 Resources
这里我们仅简单讨论了一下物理内存模型的设计思想,想要深入研究的读者可以参考如下资料:
https://docs.kernel.org/vm/memory-model.html
https://lwn.net/Articles/789304/
http://www.wowotech.net/memory_management/memory_model.html
Last updated