【原创】(四)Linux内存模型之Sparse Memory Model
2019-09-15 18:25
3727 查看
背景
Read the fucking source code!
--By 鲁迅A picture is worth a thousand words.
--By 高尔基
说明:
- Kernel版本:4.14
- ARM64处理器,Contex-A53,双核
- 使用工具:Source Insight 3.5, Visio
1. 介绍
顺着之前的分析,我们来到了
bootmem_init()函数了,本以为一篇文章能搞定,大概扫了一遍代码之后,我默默的把它拆成了两部分。
bootmem_init()函数代码如下:
void __init bootmem_init(void) { unsigned long min, max; min = PFN_UP(memblock_start_of_DRAM()); max = PFN_DOWN(memblock_end_of_DRAM()); early_memtest(min << PAGE_SHIFT, max << PAGE_SHIFT); max_pfn = max_low_pfn = max; arm64_numa_init(); /* * Sparsemem tries to allocate bootmem in memory_present(), so must be * done after the fixed reservations. */ arm64_memory_present(); sparse_init(); zone_sizes_init(min, max); memblock_dump_all(); }
这一部分,我们将研究一下
Sparse Memory Model。
在讲Linux内存模型之前,需要补充两个知识点:
PFN和
NUMA。
1.1 physical frame number(PFN)
前面我们讲述过了虚拟地址到物理地址的映射过程,而系统中对内存的管理是以页为单位的:
page:线性地址被分成以固定长度为单位的组,称为页,比如典型的4K大小,页内部连续的线性地址被映射到连续的物理地址中;
page frame:内存被分成固定长度的存储区域,称为页框,也叫物理页。每一个页框会包含一个页,页框的长度和一个页的长度是一致的,在内核中使用
struct page来关联物理页。
如下图,PFN从图片中就能看出来了:
至于
__page_to_pfn这个实现取决于具体的物理内存模型,下文将进行介绍。
1.2 NUMA
UMA: Uniform Memory Access
,所有处理器对内存的访问都是一致的:
从上图中可以看出,当处理器和Core变多的时候,内存带宽将成为瓶颈问题。
NUMA: Non Uniform Memory Access
,非一致性内存访问:
从图中可以看出,每个CPU访问local memory,速度更快,延迟更小。当然,整体的内存构成一个内存池,CPU也能访问remote memory,相对来说速度更慢,延迟更大。目前对
NUMA的了解仅限于此,在内核中会遇到相关的代码,大概知道属于什么范畴就可以了。
2. Linux内存模型
Linux提供了三种内存模型(
include/asm-generic/memory_model.h):
一般处理器架构支持一种或者多种内存模型,这个在编译阶段就已经确定,比如目前在ARM64中,使用的
Sparse Memory Model。
Flat Memory
物理内存地址连续,这个也是Linux最初使用的内存模型。当内存有空洞的时候也是可以使用这个模型,只是struct page *mem_map
数组的大小跟物理地址正相关,内存有空洞会造成浪费。Discontiguous Memory
物理内存存在空洞,随着Sparse Memory
的提出,这种内存模型也逐渐被弃用了。Sparse Memory
物理内存存在空洞,并且支持内存热插拔,以section
为单位进行管理,这也是下文将分析的。
Linux三种内存模型下,
struct page到物理
page frame的映射方式也不一样,具体可以查看
include/asm-generic/memory_model.h文件中的
__pfn_to_page/__page_to_pfn定义。
关于内存模型,可以参考Memory: the flat, the discontiguous, and the sparse
3. Sparse Memory
本节分析的是
ARM64, UMA(linux4.14中不支持ARM NUMA)下的
Sparse Memory模型。
3.1 mem_section
在
Sparse Memory模型中,
section是管理内存
online/offline的最小内存单元,在ARM64中,
section的大小为1G,而在Linux内核中,通过一个全局的二维数组
struct mem_section **mem_section来维护映射关系。
函数的调用过程如下所示,主要在
arm64_memory_present中来完成初始化及映射关系的建立:
函数调用结束之后的映射关系如下图所示:
已知一个
pfn时,可以通过
__pfn_to_section(pfn)来最终找到对应的
struct page结构。
3.2 sparse_init
看看
sparse_init函数的调用关系图:
在该函数中,首先分配了usermap,这个usermap与内存的回收机制相关,用4bit的bitmap来描述
page block(一个pageblock大小通常为2的次幂,比如MAX_ORDER-1)的迁移类型:
/* Bit indices that affect a whole block of pages */ enum pageblock_bits { PB_migrate, PB_migrate_end = PB_migrate + 3 - 1, /* 3 bits required for migrate types */ PB_migrate_skip,/* If set the block is skipped by compaction */ /* * Assume the bits will always align on a word. If this assumption * changes then get/set pageblock needs updating. */ NR_PAGEBLOCK_BITS };
sparse memory模型会为每一个section都分配一个
usermap,最终的物理页面的压缩,迁移等操作,都跟这些位相关,如下图所示:
sparse_init函数中,另一部分的作用是遍历所有
present section,然后将其映射到vmemmap区域空间。
vmemmap区域空间,在之前的文章中也提到过。执行完后,整体的效果如下图所示:
关于
Sparse Memory Model就先分析这么多,只有结合使用
sparse memory的具体模块时,理解才会更顺畅。
一不小心就容易扣细节,而一旦陷入细节,内核就容易变成魔鬼,太难了。
相关文章推荐
- 探索 Linux 内存模型
- 探索 Linux 内存模型
- 转:linux设备模型之pci设备的I/O和内存
- linux内存模型之buddy(伙伴)系统一概览及相关数据结构
- Linux内存模型
- 全面解析Linux 内核 3.10.x - 内存管理 - 内存模型
- Linux下tomcat JVM内存 配置 原创 2016年03月06日 15:10:11 4249 常见的内存溢出有以下两种: java.lang.OutOfMemoryError: PermGen
- Linux内存模型
- Linux内存模型
- Linux内存模型
- 探索 Linux 内存模型
- 探索Linux 操作系统内存模型和管理
- linux内存模型
- 探索 Linux 内存模型
- Linux内存模型
- linux内存模型初探
- linux 内存模型
- LINUX内存模型
- [原创]C++虚继承内存对象模型探讨
- 《Linux性能及调优指南》1.2 Linux内存结构【原创翻译】