首页 > 系统相关 >内存管理-18-sparsemem内存模型-初探

内存管理-18-sparsemem内存模型-初探

时间:2024-07-12 22:51:53浏览次数:21  
标签:struct mem 18 section SPARSEMEM sparsemem 内存 page

一、简介

Linux中的物理内存被按页框划分,每个页框都会对应一个 struct page 结构体存放元数据,也就是说每块页框大小的内存都要花费 sizeof(struct page) 个字节进行管理。

因此系统会有大量的 struct page,在linux的历史上出现过三种内存模型去管理它们。依次是平坦内存模型(flat memory model)、不连续内存模型 (discontiguous memory model)和稀疏内存模型(sparse memory model)。新的内存模型被提出是因为老的内存模型已不适应计算机硬件的新技术(例如:NUMA技术、内存热插拔等)。

内存模型的设计主要是权衡以下两点(空间与时间):
(1) 消耗尽量少的内存去管理众多的 struct page 数据结构。
(2) pfn_to_page() 和 page_to_pfn() 的转换效率要尽量高。


二、三种内存模型

1. FLATMEM (flat memory model)

FLATMEM 内存模型是Linux最早使用的内存模型,那时计算机的内存通常不大。Linux会使用一个 struct page mem_map[x] 的数组,以PFN为下标去依次存放所有的 strcut page 结构,且 mem_map 也位于内核空间的线性映射区,所以根据PFN(页帧号)即可轻松的找到页帧对应的 strcut page 结构。

配置选项 CONFIG_FLATMEM,没有.c实现文件。

对于物理地址空间不存在空洞(holes)的计算机来说,FLATMEM 模型无疑是最优解。可物理地址中若是存在空洞的话,FLATMEM 就显得格外的浪费内存,#########因为 FLATMEM 不得不在 mem_map[] 数组中为所有的物理地址都创建一个 struct page,即使大块的物理地址是空洞,即不存在物理内存,也要创建。


2. DISCONTIGMEM (discontiguous memory model)

为了解决不连续物理内存带来的空洞问题,Linux社区提出了 DISCONTIGMEM 模型。

DISCONTIGMEM 模型引入了内存节点的概念,这仍然是 NUMA 内存管理的基础。每个节点都带有一个独立的内存管理子系统,它有自己的空闲页面列表、正在使用的页面列表、最近最少使用 (LRU) 信息和使用情况统计信息。在所有这些好东西中,由 struct pglist_data 表示的节点数据包含特定于节点的内存映射。假设每个节点都有连续的物理内存,每个节点都有一个页面结构数组可以解决平面内存映射中存在大漏洞的问题。

配置选项 CONFIG_DISCONTIGMEM,没有.c实现文件。

然而,DISCONTIGMEM 有一个弱点:内存热插拔和热移除。实际的 NUMA 节点粒度太粗,无法正确支持热插拔,而拆分节点会产生大量不必要的碎片和开销。请记住,每个节点都有一个独立的内存管理结构以及所有相关成本,进一步拆分节点会大大增加这些成本。

DISCONTIGMEM 稍纵即逝,倍后起之秀 SPARSEMEM 完全替代。


3. SPARSEMEM (sparse memory model)

SPARSEMEM 的引入解决了这一限制。该模型将内存映射抽象为由架构定义的任意大小的部分集合。每个部分由 struct mem_section 表示,里面包含指向 struct page 数组的指针。这​​些部分的数组表示一段物理内存,可以高效地以 SECTION_SIZE 粒度进行切割。为了在 PFN 和 struct page 之间进行高效地效转换,PFN 的几个高位用于索引部分数组。对于另一个方向,部分编号被编码在页面标志中。

SPARSEMEM 将 PFN 拆分成了三个level,每个level分别对应:ROOT编号(二维数组下标)、ROOT内的section偏移(一维数组下标)、section内的page偏移。(可以类比多级页表来理解)

在 SPARSEMEM 引入 Linux 内核几个月后,被扩展为 SPARSEMEM_EXTREME,它适用于具有特别稀疏的物理地址空间的系统。SPARSEMEM_EXTREME 为 sections 数组添加了第二个维度(默认使能),变成一个二维数组。使用 SPARSEMEM_EXTREME 后,第一级变成指向 mem_section 结构数组的指针,并且根据实际物理内存动态分配。

2007 年,SPARSEMEM 又增加了一项增强功能,称为 "generic virtual memmap support for SPARSEMEM"(对SPARSEMEM 增加虚拟内存映射),配置宏为 SPARSEMEM_VMEMMAP。SPARSEMEM_VMEMMAP 背后的理念是,整个内存映射被映射到一个几乎连续的区域,但只有active部分才有物理页面支持。此模型不适用于 32 位系统,因为 32 位系统的物理内存大小可能接近甚至超过虚拟地址空间。但是,对于 64 位系统,SPARSEMEM_VMEMMAP 显然是一个优势。以额外的页表项为代价,page_to_pfn() 和 pfn_to_page() 变得与平面模型一样简单。

#if defined(CONFIG_FLATMEM) //没使能
    #define __pfn_to_page(pfn) ...
#elif defined(CONFIG_DISCONTIGMEM) //没使能
    #define __pfn_to_page(pfn) ...
#elif defined(CONFIG_SPARSEMEM_VMEMMAP) //默认使能,且优先级高
    #define __pfn_to_page(pfn) ...
#elif defined(CONFIG_SPARSEMEM) //默认使能
    #define __page_to_pfn(pg) ...
#endif

SPARSEMEM 内存模型的最后一个扩展是较新的(2016 年);它是由持久内存设备的使用增加所推动的。为了支持将内存映射直接存储在这些设备上而不是主内存中,虚拟内存映射可以使用 struct vmem_altmap,####### 它将在持久内存中提供页面结构。

2008 年,SPARSEMEM_VMEMMAP 成为 x86-64 唯一支持的内存模型,因为它仅比 FLATMEM 稍微重一点,但比 DISCONTIGMEM 更高效。最近的内存管理开发(例如内存热插拔、持久内存支持和各种性能优化)都针对 SPARSEMEM 模型。SPARSEMEM 模式也是较新Linux内核默认支持的一种内存模型。

SPARSEMEM 需要使能的配置宏和.c实现有:

CONFIG_SPARSEMEM_VMEMMAP
CONFIG_SPARSEMEM_VMEMMAP_ENABLE
CONFIG_SPARSEMEM
CONFIG_SPARSEMEM_MANUAL
CONFIG_SPARSEMEM_EXTREME
CONFIG_ARCH_SPARSEMEM_DEFAULT

msm-5.4/mm$ find ./ -name Makefile | xargs grep sparse
./Makefile:obj-$(CONFIG_SPARSEMEM)      += sparse.o
./Makefile:obj-$(CONFIG_SPARSEMEM_VMEMMAP) += sparse-vmemmap.o


三、数据结构

1. struct mem_section

SPARSEMEM 内存模型引入了section的概念,可以简单将它理解为 struct page 的集合(数组)。内核使用 struct mem_section 去描述section,定义如下:

//include/linux/mmzone.h

struct mem_section {
    unsigned long section_mem_map;
    struct mem_section_usage *usage;
#ifdef CONFIG_PAGE_EXTENSION
    struct page_ext *page_ext;
    unsigned long pad;
#endif
};

sparse_init_one_section() 中对其进行初始化。

section_mem_map: 存放的是 struct page 数组的地址,每个section可容纳 1<<PFN_SECTION_SHIFT 个 struct page. Arm64物理地址位宽为48bit时定义了每个section可囊括的地址范围是1GB(2^18个页帧,共1GB内存)。

usage:

page_ext:

pad:


2. struct mem_section_usage

struct mem_section_usage {
    DECLARE_BITMAP(subsection_map, SUBSECTIONS_PER_SECTION); //1<<9
    /* See declaration of similar field in struct zone */
    unsigned long pageblock_flags[0];
};


3. struct page_ext

struct page_ext {
    unsigned long flags;
};


四、全局变量

1. mem_section

内核中用 struct mem_section **mem_section 这个二级指针去管理section,我们可以简单理解为一个动态的二维数组。所谓二维即内核又将 SECTIONS_PER_ROOT 个section划分为一个ROOT,ROOT的个数不是固定的,根据系统实际的物理地址大小来分配。

2. section_to_node_table

static u8 section_to_node_table[NR_MEM_SECTIONS]; //1<<18


五、vmemmap 区域

vmemmap 区域是一块起始地址是 VMEMMAP_START,范围是4GB的虚拟地址空间,位于kernel space。以section为单位来存放 strcut page 结构的虚拟地址空间,然后线性映射到物理内存。

//arch/arm64/include/asm/memory.h

#define VMEMMAP_START        (-VMEMMAP_SIZE - SZ_2M) //0xfffffffeffe00000
//(0xffffffc000000000 - 0xffffff8000000000) >> (12 - 6) = 0x100000000 = 4G
#define VMEMMAP_SIZE ((_PAGE_END(VA_BITS_MIN) - PAGE_OFFSET) >> (PAGE_SHIFT - STRUCT_PAGE_MAX_SHIFT))

//启动log打印:
vmemmap : 0xfffffffeffe00000 - 0xffffffffffe00000     (     4 GB maximum)

若是有16GB的物理内存,则需要 2^34 / 2^12 * 2^6 = 256MB 的空间存储page数据结构。

 

 

 

参考:
Memory: the flat, the discontiguous, and the sparse:https://lwn.net/Articles/789304/

 

标签:struct,mem,18,section,SPARSEMEM,sparsemem,内存,page
From: https://www.cnblogs.com/hellokitty2/p/18299512

相关文章

  • C#经典面试题:执行string abc=“aaa“+“bbb“+“ccc“共分配了多少内存?
    C#经典面试题:执行stringabc="aaa"+"bbb"+"ccc"共分配了多少内存?这是一个经典的基础知识题目,它涉及了字符串的类型、堆栈和堆的内存分配机制,因此被很多人拿来考核开发者的基础知识功底。首先,我们都知道,判断值类型的标准是查看该类型是否会继承自System.ValueType,通过查看和......
  • 动态内存管理
    为什么要有动态内存分配我们已经掌握的内存开辟⽅式有:如上图所示,有两种开辟空间的方法,一种是在栈空间上开辟一个整型变量,一种是在栈空间上开辟一个数组。但是上述的开辟空间的⽅式有两个特点:空间开辟⼤⼩是固定的。数组在声明的时候,必须指定数组的⻓......
  • 关于Java内存区域的理解和记录
    近期做项目遇到了FullGC的问题,干脆总结一下Java内存区域分布和垃圾回收是咋回事。Java内存区域按照线程隔离状态直接分成三大块空间:线程私有:程序计数器是一块较小的内存空间,可以看做当前线程所执行的字节码的行号指示器。在虚拟机概念模型里,字节码解释器工作时就是通过改变这......
  • ubuntu 18.04 安装 腾讯原生微信
    使用终端命令行安装铜豌豆软件源。注意需要用到sudo权限。```textwget-c-Oatzlinux-v12-archive-keyring_lastest_all.debhttps://www.atzlinux.com/atzlinux/pool/main/a/atzlinux-archive-keyring/atzlinux-v12-archive-keyring_lastest_all.debsudoapt-yinstall./......
  • ObjectiveC 内存管理
    内存的五大区域栈:局部变量(当局部变量的作用域被执行完毕之后,这个局部变量就会被系统立即回收);堆:OC对象和使用C函数申请的空间;BSS段:未初始化的全局变量、静态变量,一旦初始化就回收并转存到数据段之中;数据段:已经初始化的全局变量、静态变量,直到程序结束的时候才会被回收;代码段:二......
  • 基于QT开发的反射内存小工具
    前言最近项目需要需要开发一个反射内存小工具,经过2天的修修改终于完成了。界面如下:功能简介反射内存指定地址数据读取反射内存指定地址数据写入反射内存指定地址数据清理十进制、十六进制、二进制数据相互转换部分代码voidRfmMain::setWOtherEditData(constQStri......
  • pwnable.tw | 第3题cve-2018-1106
    前言pwnable.tw第三题,开始上难度了,考验的是一款真实程序的漏洞利用。漏洞源于一款开源的苹果AFP协议服务器程序Netatalk,漏洞最早是2018年发现的,2019年在HITCONQuals展示了1day利用,相关文章如下:2018年漏洞作者的原文翻译:NetatalkCVE-2018-1160的发现与利用hitcon相关文章:HITC......
  • C++内存管理
    1内存概述1.1分配方式在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。栈,在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量......
  • C++ 避免内存泄露的手段和措施
    在C++中,内存泄露是一个常见问题,指的是已分配的内存由于某种原因未被释放,导致程序无法再次使用这部分内存。为了避免内存泄露,C++提供了多种手段和措施,主要包括以下几种:智能指针(SmartPointers):智能指针是C++标准库中的一部分,用于自动管理内存,确保在适当的时候释放内存。......
  • Day6 (哈希表)| 454.四数相加II 383.赎金信 15.三数之和 18.四数之和
    454.四数相加II给你四个整数数组nums1、nums2、nums3和nums4,数组长度都是n,请你计算有多少个元组(i,j,k,l)能满足:0<=i,j,k,l<nnums1[i]+nums2[j]+nums3[k]+nums4[l]==0示例1:输入:nums1=[1,2],nums2=[-2,-1],nums3=[-1,2],nums4=[0,2]......