1 关于Linux内存模型的介绍-德赢Vwin官网 网
0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

关于Linux内存模型的介绍

Linux阅码场 来源:未知 作者:工程师郭婷 2018-07-18 16:26 次阅读

一、前言

linux内核中支持3中内存模型,分别是flat memory model,Discontiguous memory model和sparse memory model。所谓memory model,其实就是从cpu的角度看,其物理内存的分布情况,在linux kernel中,使用什么的方式来管理这些物理内存。

二、和内存模型相关的术语

1、什么是page frame?

操作系统最重要的作用之一就是管理计算机系统中的各种资源,做为最重要的资源:内存,我们必须管理起来。在linux操作系统中,物理内存是按照page size来管理的,具体page size是多少是和硬件以及linux系统配置相关的,4k是最经典的设定。因此,对于物理内存,我们将其分成一个个按page size排列的page,每一个物理内存中的page size的内存区域我们称之page frame。我们针对每一个物理的page frame建立一个struct page的数据结构来跟踪每一个物理页面的使用情况:是用于内核的正文段?还是用于进程的页表?是用于各种file cache还是处于free状态……

每一个page frame有一个一一对应的page数据结构,系统中定义了page_to_pfn和pfn_to_page的宏用来在page frame number和page数据结构之间进行转换,具体如何转换是和memory modle相关,我们会在第三章详细描述linux kernel中的3种内存模型。

2、什么是PFN?

对于一个计算机系统,其整个物理地址空间应该是从0开始,到实际系统能支持的最大物理空间为止的一段地址空间。在ARM系统中,假设物理地址是32个bit,那么其物理地址空间就是4G,在ARM64系统中,如果支持的物理地址bit数目是48个,那么其物理地址空间就是256T。当然,实际上这么大的物理地址空间并不是都用于内存,有些也属于I/O空间(当然,有些cpu arch有自己独立的io address space)。因此,内存所占据的物理地址空间应该是一个有限的区间,不可能覆盖整个物理地址空间。不过,现在由于内存越来越大,对于32位系统,4G的物理地址空间已经无法满足内存的需求,因此会有high memory这个概念,后续会详细描述。

PFN是page frame number的缩写,所谓page frame,就是针对物理内存而言的,把物理内存分成一个个的page size的区域,并且给每一个page 编号,这个号码就是PFN。假设物理内存从0地址开始,那么PFN等于0的那个页帧就是0地址(物理地址)开始的那个page。假设物理内存从x地址开始,那么第一个页帧号码就是(x>>PAGE_SHIFT)。

3、什么是NUMA?

在为multiprocessors系统设计内存架构的时候有两种选择:一种就是UMA(Uniform memory access),系统中的所有的processor共享一个统一的,一致的物理内存空间,无论从哪一个processor发起访问,对内存地址的访问时间都是一样的。NUMA(Non-uniform memory access)和UMA不同,对某个内存地址的访问是和该memory与processor之间的相对位置有关的。例如,对于某个节点(node)上的processor而言,访问local memory要比访问那些remote memory的速度要快。

三、Linux 内核中的三种memory model

1、什么是FLAT memory model?

如果从系统中任意一个processor的角度来看,当它访问物理内存的时候,物理地址空间是一个连续的,没有空洞的地址空间,那么这种计算机系统的内存模型就是Flat memory。这种内存模型下,物理内存的管理比较简单,每一个物理页帧都会有一个page数据结构来抽象,因此系统中存在一个struct page的数组(mem_map),每一个数组条目指向一个实际的物理页帧(page frame)。在flat memory的情况下,PFN(page frame number)和mem_map数组index的关系是线性的(有一个固定偏移,如果内存对应的物理地址等于0,那么PFN就是数组index)。因此从PFN到对应的page数据结构是非常容易的,反之亦然,具体可以参考page_to_pfn和pfn_to_page的定义。此外,对于flat memory model,节点(struct pglist_data)只有一个(为了和Discontiguous Memory Model采用同样的机制)。下面的图片描述了flat memory的情况:

关于Linux内存模型的介绍

需要强调的是struct page所占用的内存位于直接映射(directly mapped)区间,因此操作系统不需要再为其建立page table。

2、什么是Discontiguous Memory Model?

如果cpu在访问物理内存的时候,其地址空间有一些空洞,是不连续的,那么这种计算机系统的内存模型就是Discontiguous memory。一般而言,NUMA架构的计算机系统的memory model都是选择Discontiguous Memory,不过,这两个概念其实是不同的。NUMA强调的是memory和processor的位置关系,和内存模型其实是没有关系的,只不过,由于同一node上的memory和processor有更紧密的耦合关系(访问更快),因此需要多个node来管理。Discontiguous memory本质上是flat memory内存模型的扩展,整个物理内存的address space大部分是成片的大块内存,中间会有一些空洞,每一个成片的memory address space属于一个node(如果局限在一个node内部,其内存模型是flat memory)。下面的图片描述了Discontiguous memory的情况:

关于Linux内存模型的介绍

因此,这种内存模型下,节点数据(struct pglist_data)有多个,宏定义NODE_DATA可以得到指定节点的struct pglist_data。而,每个节点管理的物理内存保存在struct pglist_data 数据结构的node_mem_map成员中(概念类似flat memory中的mem_map)。这时候,从PFN转换到具体的struct page会稍微复杂一点,我们首先要从PFN得到node ID,然后根据这个ID找到对于的pglist_data 数据结构,也就找到了对应的page数组,之后的方法就类似flat memory了。

3、什么是Sparse Memory Model?

Memory model也是一个演进过程,刚开始的时候,使用flat memory去抽象一个连续的内存地址空间(mem_maps[]),出现NUMA之后,整个不连续的内存空间被分成若干个node,每个node上是连续的内存地址空间,也就是说,原来的单一的一个mem_maps[]变成了若干个mem_maps[]了。一切看起来已经完美了,但是memory hotplug的出现让原来完美的设计变得不完美了,因为即便是一个node中的mem_maps[]也有可能是不连续了。其实,在出现了sparse memory之后,Discontiguous memory内存模型已经不是那么重要了,按理说sparse memory最终可以替代Discontiguous memory的,这个替代过程正在进行中,4.4的内核仍然是有3中内存模型可以选择。

为什么说sparse memory最终可以替代Discontiguous memory呢?实际上在sparse memory内存模型下,连续的地址空间按照SECTION(例如1G)被分成了一段一段的,其中每一section都是hotplug的,因此sparse memory下,内存地址空间可以被切分的更细,支持更离散的Discontiguous memory。此外,在sparse memory没有出现之前,NUMA和Discontiguous memory总是剪不断,理还乱的关系:NUMA并没有规定其内存的连续性,而Discontiguous memory系统也并非一定是NUMA系统,但是这两种配置都是multi node的。有了sparse memory之后,我们终于可以把内存的连续性和NUMA的概念剥离开来:一个NUMA系统可以是flat memory,也可以是sparse memory,而一个sparse memory系统可以是NUMA,也可以是UMA的。

下面的图片说明了sparse memory是如何管理page frame的(配置了SPARSEMEM_EXTREME):

关于Linux内存模型的介绍

(注意:上图中的一个mem_section指针应该指向一个page,而一个page中有若干个struct mem_section数据单元)

整个连续的物理地址空间是按照一个section一个section来切断的,每一个section内部,其memory是连续的(即符合flat memory的特点),因此,mem_map的page数组依附于section结构(struct mem_section)而不是node结构了(struct pglist_data)。当然,无论哪一种memory model,都需要处理PFN和page之间的对应关系,只不过sparse memory多了一个section的概念,让转换变成了PFN<--->Section<--->page。

我们首先看看如何从PFN到page结构的转换:kernel中静态定义了一个mem_section的指针数组,一个section中往往包括多个page,因此需要通过右移将PFN转换成section number,用section number做为index在mem_section指针数组可以找到该PFN对应的section数据结构。找到section之后,沿着其section_mem_map就可以找到对应的page数据结构。顺便一提的是,在开始的时候,sparse memory使用了一维的memory_section数组(不是指针数组),这样的实现对于特别稀疏(CONFIG_SPARSEMEM_EXTREME)的系统非常浪费内存。此外,保存指针对hotplug的支持是比较方便的,指针等于NULL就意味着该section不存在。上面的图片描述的是一维mem_section指针数组的情况(配置了SPARSEMEM_EXTREME),对于非SPARSEMEM_EXTREME配置,概念是类似的,具体操作大家可以自行阅读代码。

从page到PFN稍微有一点麻烦,实际上PFN分成两个部分:一部分是section index,另外一个部分是page在该section的偏移。我们需要首先从page得到section index,也就得到对应的memory_section,知道了memory_section也就知道该page在section_mem_map,也就知道了page在该section的偏移,最后可以合成PFN。对于page到section index的转换,sparse memory有2种方案,我们先看看经典的方案,也就是保存在page->flags中(配置了SECTION_IN_PAGE_FLAGS)。这种方法的最大的问题是page->flags中的bit数目不一定够用,因为这个flag中承载了太多的信息,各种page flag,node id,zone id现在又增加一个section id,在不同的architecture中无法实现一致性的算法,有没有一种通用的算法呢?这就是CONFIG_SPARSEMEM_VMEMMAP。具体的算法可以参考下图:

关于Linux内存模型的介绍

(上面的图片有一点问题,vmemmap只有在PHYS_OFFSET等于0的情况下才指向第一个struct page数组,一般而言,应该有一个offset的,不过,懒得改了,哈哈)

对于经典的sparse memory模型,一个section的struct page数组所占用的内存来自directly mapped区域,页表在初始化的时候就建立好了,分配了page frame也就是分配了虚拟地址。但是,对于SPARSEMEM_VMEMMAP而言,虚拟地址一开始就分配好了,是vmemmap开始的一段连续的虚拟地址空间,每一个page都有一个对应的struct page,当然,只有虚拟地址,没有物理地址。因此,当一个section被发现后,可以立刻找到对应的struct page的虚拟地址,当然,还需要分配一个物理的page frame,然后建立页表什么的,因此,对于这种sparse memory,开销会稍微大一些(多了个建立映射的过程)。

四、代码分析

我们的代码分析主要是通过include/asm-generic/memory_model.h展开的。

1、flat memory。代码如下:

关于Linux内存模型的介绍

由代码可知,PFN和struct page数组(mem_map)index是线性关系,有一个固定的偏移就是ARCH_PFN_OFFSET,这个偏移是和估计的architecture有关。对于ARM64,定义在arch/arm/include/asm/memory.h文件中,当然,这个定义是和内存所占据的物理地址空间有关(即和PHYS_OFFSET的定义有关)。

2、Discontiguous Memory Model。代码如下:

关于Linux内存模型的介绍

Discontiguous Memory Model需要获取node id,只要找到node id,一切都好办了,比对flat memory model进行就OK了。因此对于__pfn_to_page的定义,可以首先通过arch_pfn_to_nid将PFN转换成node id,通过NODE_DATA宏定义可以找到该node对应的pglist_data数据结构,该数据结构的node_start_pfn记录了该node的第一个page frame number,因此,也就可以得到其对应struct page在node_mem_map的偏移。__page_to_pfn类似,大家可以自己分析。

3、Sparse Memory Model。经典算法的代码我们就不看了,一起看看配置了SPARSEMEM_VMEMMAP的代码,如下:

关于Linux内存模型的介绍

简单而清晰,PFN就是vmemmap这个struct page数组的index啊。对于ARM64而言,vmemmap定义如下:

关于Linux内存模型的介绍

毫无疑问,我们需要在虚拟地址空间中分配一段地址来安放struct page数组(该数组包含了所有物理内存跨度空间page),也就是VMEMMAP_START的定义。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10854

    浏览量

    211563
  • 内存
    +关注

    关注

    8

    文章

    3019

    浏览量

    74000
  • LINUX内核
    +关注

    关注

    1

    文章

    316

    浏览量

    21644

原文标题:郭健: Linux内存模型——平坦、非连续与稀疏

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Linux内存管理是什么,Linux内存管理详解

    Linux内存管理 Linux内存管理是一个非常复杂的过程,主要分成两个大的部分:内核的内存管理和进程虚拟
    的头像 发表于 05-11 17:54 6038次阅读
    <b class='flag-5'>Linux</b>的<b class='flag-5'>内存</b>管理是什么,<b class='flag-5'>Linux</b>的<b class='flag-5'>内存</b>管理详解

    走进Linux内存系统探寻内存管理的机制和奥秘

    Linux 内存是后台开发人员,需要深入了解的计算机资源。合理的使用内存,有助于提升机器的性能和稳定性。本文主要介绍Linux
    的头像 发表于 01-05 09:47 1618次阅读

    关于Linux内存管理的详细介绍

    Linux内存管理是指对系统内存的分配、释放、映射、管理、交换、压缩等一系列操作的管理。在Linux中,内存被划分为多个区域,每个区域有不同
    发表于 03-06 09:28 1064次阅读

    Linux内存相关知识科普

    Linux 内存是后台开发人员,需要深入了解的计算机资源。合理的使用内存,有助于提升机器的性能和稳定性。本文主要介绍**Linu****x 内存
    发表于 07-25 14:43 723次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内存</b>相关知识科普

    物理内存模型的演变

    内存管理概述中,主要是以Linux v2.6.11为例进行分析的,但是计算技术在不断发展,新的存储架构、新的指令集架构、新的SoC架构等都对物理内存模型的抽象提出了更高要求。为此,必须
    的头像 发表于 02-25 10:35 469次阅读

    Linux内核地址映射模型Linux内核高端内存详解

    的数据可能不在内存中。 Linux内核地址映射模型 x86 CPU采用了段页式地址映射模型。进程代码中的地址为逻辑地址,经过段页式地址映射后,才真正访问物理
    发表于 05-08 10:33 3452次阅读
    <b class='flag-5'>Linux</b>内核地址映射<b class='flag-5'>模型</b>与<b class='flag-5'>Linux</b>内核高端<b class='flag-5'>内存</b>详解

    RTOS和Linux中的内存映射及移植方法

    /O方法。它们涵盖的范围从对中断服务例程的特殊使用及用户线程对硬件访问到出现于有些ROTS中的半规范化驱动程序模型。这对于移植RTOS 代码到规范化的Linux设备启动程序具有一定启发作用,并且介绍
    发表于 07-03 07:43

    Armv8-A内存定序模型详解

    1. 本指南介绍由 Armv8-A 架构定义的内存定序模型,并介绍所提供的不同内存障碍;本指南还确定一些需要明确定序的常见情况,以及如何使用
    发表于 08-02 11:03

    linux内存管理机制浅析

    本内容介绍了arm linux内存管理机制,详细说明了linux内核内存管理,linux虚拟
    发表于 12-19 14:09 73次下载
    <b class='flag-5'>linux</b><b class='flag-5'>内存</b>管理机制浅析

    基于ARM_CPU的Linux物理内存管理

    关于arm cpu内存介绍
    发表于 02-15 23:53 5次下载

    了解并学习Linux内存模型

    linux内核中支持3中内存模型,分别是flat memory model,Discontiguous memory model和sparse memory model。所谓memory
    发表于 05-12 09:44 690次阅读
    了解并学习<b class='flag-5'>Linux</b><b class='flag-5'>内存</b><b class='flag-5'>模型</b>

    一文解析Linux内存系统

    Linux 内存是后台开发人员,需要深入了解的计算机资源。合理的使用内存,有助于提升机器的性能和稳定性。本文主要介绍Linux
    的头像 发表于 09-01 10:46 2437次阅读
    一文解析<b class='flag-5'>Linux</b><b class='flag-5'>内存</b>系统

    深入剖析Linux共享内存原理

    不同进程之间进行通信,需要让不同进程共享相同的物理内存Linux通过  共享内存  来实现这个功能。下面先来介绍一下Linux系统的共享
    的头像 发表于 10-30 09:52 2277次阅读
    深入剖析<b class='flag-5'>Linux</b>共享<b class='flag-5'>内存</b>原理

    Linux系统的共享内存的使用

    但有时候为了让不同进程之间进行通信,需要让不同进程共享相同的物理内存Linux通过 共享内存 来实现这个功能。下面先来介绍一下Linux
    的头像 发表于 11-14 11:55 1315次阅读

    Linux 内存管理总结

    一、Linux内存管理概述 Linux内存管理是指对系统内存的分配、释放、映射、管理、交换、压缩等一系列操作的管理。在
    的头像 发表于 11-10 14:58 523次阅读
    <b class='flag-5'>Linux</b> <b class='flag-5'>内存</b>管理总结