背景

  • Read the fucking source code! --By 鲁迅
  • A picture is worth a thousand words. --By 高尔基

说明:

  1. Kernel版本:4.14
  2. ARM64处理器,Contex-A53,双核
  3. 使用工具: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内存模型之前,需要补充两个知识点:PFNNUMA

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内存模型之Sparse Memory Model的更多相关文章

  1. JAVA内存模型(Java Memory Model ,JMM)

    http://blog.csdn.net/hxpjava1/article/details/55189077 JVM有主内存(Main Memory)和工作内存(Working Memory),主内存 ...

  2. 并发研究之Java内存模型(Java Memory Model)

    Java内存模型JMM java内存模型定义 上一遍文章我们讲到了CPU缓存一致性以及内存屏障问题.那么Java作为一个跨平台的语言,它的实现要面对不同的底层硬件系统,设计一个中间层模型来屏蔽底层的硬 ...

  3. 浅析java内存模型--JMM(Java Memory Model)

    在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步? 在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的. 线程之间通过共享程序公共的状态,通 ...

  4. 并发编程之java内存模型(Java Memory Model ,JMM)

    一.图例 0.两个概念 Heap(堆):运行时的数据区,由垃圾回收负责,运行时分配内存(所以慢),对象存放在堆上 如果两个线程,同时调用同一个变量,怎两个线程都拥有,该对象的私有拷贝 (可以看一下,T ...

  5. java内存模型(Java Memory Model)

    内容导航: Java内存模型 硬件存储体系结构 Java内存模型和硬件存储体系之间的桥梁: 共享对象的可见性 竞争条件 Java内存模型规定了JVM怎样与计算机存储系统(RAM)协调工作.JVM是一个 ...

  6. Java 内存模型(Java Memory Model,JMM)

    基本概念 JMM 本身是一种抽象的概念并不是真实存在,它描述的是一组规范,通过这组规范定义了程序的访问方式 JMM 同步规定 线程解锁前,必须把共享变量的值刷新回主内存 线程加锁前,必须读取主内存的最 ...

  7. 探索 Linux 内存模型--转

    引用:http://www.ibm.com/developerworks/cn/linux/l-memmod/index.html 理解 Linux 使用的内存模型是从更大程度上掌握 Linux 设计 ...

  8. 【ARM-Linux开发】Linux内存管理:ARM Memory Layout以及mmu配置

    原文:Linux内存管理:ARM Memory Layout以及mmu配置 在内核进行page初始化以及mmu配置之前,首先需要知道整个memory map. 1. ARM Memory Layout ...

  9. Linux内存模型

    http://blog.csdn.net/sunyubo458/article/details/6090946 了解linux的内存模型,或许不能让你大幅度提高编程能力,但是作为一个基本知识点应该熟悉 ...

随机推荐

  1. HomeKit智能球泡

    产品名称: 智能LED灯泡调光调色 接入苹果HomeKit家庭(无需网关).天猫精灵.小爱.小度.Google.ALEXA 产品价格:9.9 本产品是针对HomeKit的产品,没有iphone手机,配 ...

  2. 解决!!-- krb5-libs.x86_64被卸载,yum不能使用,ssh不能连接

    常在河边走哪有不湿鞋,常玩服务器哪有不搞挂几台,一不小心就搞挂了 今天删除 krb5-libs.x86_64下了狠功夫..... 用了命令: rpm -e --nodeps  krb5-libs.x8 ...

  3. spring cloud 断路器 Hystrix

    一.微服务架构中使用断路器的原因 二.代码实现 1.在Ribbon中使用短路器 1.1.引入依赖 <dependency> <groupId>org.springframewo ...

  4. 驰骋工作流引擎ccflow-流转自定义功能使用说明

    流转自定义功能使用说明 关键字: 驰骋工作流程快速开发平台 工作流程管理系统 工作流引擎 asp.net工作流引擎 java工作流引擎. 节点跳转 节点流转自定义 应用背景: 有一些流程在运行过程中是 ...

  5. Android老司机搬砖小技巧

    作为一名Android世界的搬运工,每天搬砖已经够苦够累了,走在坑坑洼洼的道路一不小心就掉坑里了. SDK常用工具类 Android SDK中本身就拥有很多轮子,熟悉这些轮子,可以提高我们的搬砖效率. ...

  6. python学习之路(2)---字符编码

    二进制编码 bin(300)    python计算二进制编码,十进制转2进制 一个二进制位就是1bit 1bit代表了8个字节,00001111 1bit  = 1bytes   缩写1b 1kb ...

  7. day01计算机硬件基础

    1.cpu\内存\硬盘 2.存储器 操作系统 是什么 为什么 如何用 1.什么是编程语言: 语言是一个事物与另一个事物沟通的表达方式 编程语言即程序员与计算机沟通的介质 计算机是程序员的奴隶 2.什么 ...

  8. Qt最新版5.12.2在Win10环境静态编译安装和部署的完整过程(VS2017)

    一.为什么要静态编译 用QtCreator编译程序时,使用的是动态编译.编译好的程序在运行时需要另外加上相应的Qt库文件,一大堆dll文件.如果想将生成的程序连同所需要的库一起完整地打包成一个可执行程 ...

  9. UVA 10699 Count the factors 题解

    Time limit 3000 ms OS Linux Write a program, that computes the number of different prime factors in ...

  10. 章节十六、5-TestNG高级功能--Part2

    一.测试用例的依赖关系--->(dependsOnMethods = {"依赖方法名"}) 1.在实现自动化的过程中,有些测试用例必须在其它测试用例执行之后才能运行,两者之间 ...