深入理解linux物理内存

news2024/11/14 14:52:41

目录

物理内存热插拔

从 CPU 角度看物理内存架构

内核如何管理 NUMA 节点

 NUMA  节点物理内存区域的划分

NUMA 节点的状态 node_states

 物理内存区域中的水位线

物理内存区域中的冷热页 

 内核如何描述物理内存页

匿名页的反向映射


物理内存热插拔

  • 物理热插拔阶段:这个阶段主要是从物理上将内存硬件插入(hot-add),拔出(hot-remove)主板的过程,其中涉及到硬件和内核的支持。

  • 逻辑热插拔阶段:这一阶段主要是由内核中的内存管理子系统来负责,涉及到的主要工作为:如何动态的上线启用(online)刚刚 hot-add 的内存,如何动态下线(offline)刚刚 hot-remove 的内存。

  • 但是这里会有一个问题,就是并非所有的物理页都可以迁移,因为迁移意味着物理内存地址的变化,而内存的热插拔应该对进程来说是透明的,所以这些迁移后的物理页映射的虚拟内存地址是不能变化的。

    这一点在进程的用户空间是没有问题的,因为进程在用户空间访问内存都是根据虚拟内存地址通过页表找到对应的物理内存地址,这些迁移之后的物理页,虽然物理内存地址发生变化,但是内核通过修改相应页表中虚拟内存地址与物理内存地址之间的映射关系,可以保证虚拟内存地址不会改变

    既然是这些不可迁移的物理页导致内存无法拔出,那么我们可以把内存分一下类,将内存按照物理页是否可迁移,划分为不可迁移页,可回收页,可迁移页。

    大家这里需要记住一点,内核会将物理内存按照页面是否可迁移的特性进行分类,笔者后面在介绍内核如何避免内存碎片的时候还会在提到

    然后在这些可能会被拔出的内存中只分配那些可迁移的内存页,这些信息会在内存初始化的时候被设置,这样一来那些不可迁移的页就不会包含在可能会拔出的内存中,当我们需要将这块内存热拔出时, 因为里边的内存页全部是可迁移的, 从而使内存可以被拔除。

从 CPU 角度看物理内存架构

UMA架构:

  1. 总线的带宽压力会越来越大,随着 CPU 个数的增多导致每个 CPU 可用带宽会减少

  2. 总线的长度也会因此而增加,进而增加访问延迟

在 NUMA 架构下,内存就不是一整片的了,而是被划分成了一个一个的内存节点 (NUMA 节点),每个 CPU 都有属于自己的本地内存节点,CPU 访问自己的本地内存不需要经过总线,因此访问速度是最快的。当 CPU 自己的本地内存不足时,CPU 就需要跨节点去访问其他内存节点,这种情况下 CPU 访问内存就会慢很多。

在 NUMA 架构下,任意一个 CPU 都可以访问全部的内存节点,访问自己的本地内存节点是最快的,但访问其他内存节点就会慢很多,这就导致了 CPU 访问内存的速度不一致,所以叫做非一致性内存访问架构。

 CPU 和它的本地内存组成了 NUMA 节点,CPU 与 CPU 之间通过 QPI(Intel QuickPath Interconnect)点对点完成互联,在 CPU  的本地内存不足的情况下,CPU 需要通过 QPI 访问远程 NUMA 节点上的内存控制器从而在远程内存节点上分配内存,这就导致了远程访问比本地访问多了额外的延迟开销(需要通过 QPI 遍历远程 NUMA 节点)。

内核如何管理 NUMA 节点

在内核中是如何将这些 NUMA 节点统一管理起来的?struct pglist_data 这样的一个数据结构来描述 NUMA 节点,在内核 2.4 版本之前,内核是使用一个 pgdat_list 单链表将这些 NUMA 节点串联起来的。在内核 2.4 之后的版本中,内核移除了 struct pglist_data 结构中的 pgdat_next 之指针, 同时也删除了 pgdat_list 单链表。取而代之的是,内核使用了一个大小为 MAX_NUMNODES ,类型为 struct pglist_data 的全局数组 node_data[] 来管理所有的 NUMA 节点。

typedef struct pglist_data {
    // NUMA 节点id
    int node_id;
    // 指向 NUMA 节点内管理所有物理页 page 的数组
    struct page *node_mem_map;
    // NUMA 节点内第一个物理页的 pfn
    unsigned long node_start_pfn;
    // NUMA 节点内所有可用的物理页个数(不包含内存空洞)
    unsigned long node_present_pages;
    // NUMA 节点内所有的物理页个数(包含内存空洞)
    unsigned long node_spanned_pages; 
    // 保证多进程可以并发安全的访问 NUMA 节点
    spinlock_t node_size_lock;
        .............
}

 

 NUMA  节点物理内存区域的划分

NUMA 节点的状态 node_states

如果系统中的 NUMA 节点多于一个,内核会维护一个位图 node_states,用于维护各个 NUMA 节点的状态信息。 表示 NUMA 节点在某个时刻可以变为 online 状态,N_ONLINE 表示 NUMA 节点当前的状态为 online 状态。

 物理内存区域中的水位线

物理内存区域中的冷热页 

 内核如何描述物理内存页

  1. 一种是匿名页,匿名页背后并没有一个磁盘中的文件作为数据来源,匿名页中的数据都是通过进程运行过程中产生的,匿名页直接和进程虚拟地址空间建立映射供进程使用。

  2. 另外一种是文件页,文件页中的数据来自于磁盘中的文件,文件页需要先关联一个磁盘中的文件,然后再和进程虚拟地址空间建立映射供进程使用,使得进程可以通过操作虚拟内存实现对文件的操作,这就是我们常说的内存文件映射。

匿名页的反向映射

我们通常所说的内存映射是正向映射,即从虚拟内存到物理内存的映射。而反向映射则是从物理内存到虚拟内存的映射,用于当某个物理内存页需要进行回收或迁移时,此时需要去找到这个物理页被映射到了哪些进程的虚拟地址空间中,并断开它们之间的映射。

struct anon_vma_chain 结构通过其中的 vma 指针和 anon_vma 指针将相关的匿名页与其映射的进程虚拟内存空间关联了起来。从目前来看匿名页 struct page 算是与 anon_vma 建立了关系,又通过 anon_vma_chain 将 anon_vma 与 vm_area_struct 建立了关系。那么就剩下最后一道关系需要打通了,就是如何通过 anon_vma 找到 anon_vma_chain 进而找到 vm_area_struct 呢?这就需要我们将 anon_vma 与 anon_vma_chain 之间的关系也打通。 

我们知道每个匿名页对应唯一的 anon_vma 结构,但是一个匿名物理页可以映射到不同进程的虚拟内存空间中,每个进程的虚拟内存空间都是独立的,也就是说不同的进程就会有不同的 VMA。

不同的 VMA 意味着同一个匿名页 anon_vma 就会对应多个 anon_vma_chain。那么如何通过一个 anon_vma 找到和他关联的所有 anon_vma_chain 呢?找到了这些 anon_vma_chain 也就意味着 struct page 找到了与它关联的所有进程虚拟内存空间 VMA。

我们重点来看 struct anon_vma 结构中的 rb_root 字段,struct anon_vma 结构中管理了一颗红黑树,这颗红黑树上管理的全部都是与该 anon_vma 关联的 anon_vma_chain。我们可以通过 struct page 中的 mapping 指针找到 anon_vma,然后遍历 anon_vma 中的这颗红黑树 rb_root ,从而找到与其关联的所有 anon_vma_chain。

到目前为止,物理内存页 page 到与其映射的进程虚拟内存空间 VMA,这样一种一对多的映射关系现在就算建立起来了。 

 vm_area_struct 表示的只是进程虚拟内存空间中的一段虚拟内存区域,这块虚拟内存区域中可能会包含多个匿名页,所以 VMA 与物理内存页 page 也是有一对多的映射关系存在。而这个映射关系在哪里保存呢?

大家注意 struct anon_vma_chain 结构中还有一个列表结构 same_vma,从这个名字上我们很容易就能猜到这个列表 same_vma 中存储的 anon_vma_chain 对应的 VMA 全都是一样的,而列表元素 anon_vma_chain 中的 anon_vma 却是不一样的。内核用这样一个链表结构 same_vma 存储了进程相应虚拟内存区域 VMA 中所包含的所有匿名页。

到现在为止还缺关键的最后一步,就是打通匿名内存页 page 到 vm_area_struct 之间的关系,首先我们就需要调用 alloc_zeroed_user_highpage_movable 方法从伙伴系统中申请一个匿名页。当获取到 page 实例之后,通过 page_add_new_anon_rmap 最终建立起 page 到 vm_area_struct 的整条反向映射链路。 

如果当前物理内存页 struct page 是一个匿名页的话,那么 mapping 指针的最低位会被设置为 1 , 指向该匿名页在进程虚拟内存空间中的匿名映射区域 struct anon_vma 结构(每个匿名页对应唯一的 anon_vma 结构),用于物理内存到虚拟内存的反向映射。

如果当前物理内存页 struct page 是一个文件页的话,那么 mapping 指针的最低位会被设置为 0 ,指向该内存页关联文件的 struct address_space(页高速缓存)。pgoff_t index 字段表示该内存页 page 在页高速缓存中的 index 索引,也表示该内存页中的文件数据在文件内部的偏移 offset。偏移单位为 page size。

内核可以通过这个技巧直接检查 page 结构中的 mapping 指针的最低位来判断该物理内存页到底是匿名页还是文件页

struct page {
    struct address_space *mapping; 
    pgoff_t index;  
    // 表示该 page 映射了多少个进程的虚拟内存空间,一个 page 可以被多个进程映射
    atomic_t _mapcount
}

经过本小节详细的介绍,我想大家现在已经猜到 _mapcount 字段的含义了,我们知道一个物理内存页可以映射到多个进程的虚拟内存空间中,比如:共享内存映射,父子进程的创建等。page 与 VMA 是一对多的关系,这里的 _mapcount 就表示该物理页映射到了多少个进程的虚拟内存空间中。

内存回收的关键是如何实现一个高效的页面替换算法 PFRA (Page Frame Replacement Algorithm) ,提到页面替换算法大家可能立马会想到  LRU (Least-Recently-Used) 算法。LRU 算法的核心思想就是那些最近最少使用的页面,在未来的一段时间内可能也不会再次被使用,所以在内存紧张的时候,会优先将这些最近最少使用的页面置换出去。在这种情况下其实一个 active 链表就可以满足我们的需求。

四种 LRU 链表(匿名页的 active 链表,inactive 链表和文件页的active 链表, inactive 链表)之外,内核还有一种链表,比如进程可以通过 mlock() 等系统调用把内存页锁定在内存里,保证该内存页无论如何不会被置换出去,比如出于安全或者性能的考虑,页面中可能会包含一些敏感的信息不想被 swap 到磁盘上导致泄密,或者一些频繁访问的内存页必须一直贮存在内存中。

工作原理如下:

  1. 首先 inactive 链表的尾部存放的是访问频率最低并且最少访问的页面,在内存紧张的时候,这些页面被置换出去的优先级是最大的。

  2. 对于文件页来说,当它被第一次读取的时候,内核会将它放置在 inactive 链表的头部,如果它继续被访问,则会提升至 active 链表的尾部。如果它没有继续被访问,则会随着新文件页的进入,内核会将它慢慢的推到  inactive 链表的尾部,如果此时再次被访问则会直接被提升到 active 链表的头部。大家可以看出此时页面的使用频率这个因素已经被考量了进来。

  3. 对于匿名页来说,当它被第一次读取的时候,内核会直接将它放置在 active 链表的尾部,注意不是 inactive 链表的头部,这里和文件页不同。因为匿名页的换出 Swap Out 成本会更大,内核会对匿名页更加优待。当匿名页再次被访问的时候就会被被提升到 active 链表的头部。

  4. 当遇到内存紧张的情况需要换页时,内核会从 active 链表的尾部开始扫描,将一定量的页面降级到  inactive 链表头部,这样一来原来位于 inactive 链表尾部的页面就会被置换出去。

内核在回收内存的时候,这两个列表中的回收优先级为:inactive 链表尾部 > inactive 链表头部 > active 链表尾部 > active 链表头部。

参考文献

一步一图带你深入理解 Linux 物理内存管理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/729360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

『赠书活动 | 第十四期』《Spring Cloud Alibaba核心技术与实战案例》

💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! 『赠书活动 | 第十四期』 本期书籍:《Spring Cloud Alibaba核心技术与实战案例》 公众号赠书:第五期 参与方式:关注公…

【Linux 驱动篇(四)】设备树

文章目录 一、什么是设备树二、DTS、 DTB 和 DTC三、DTS 语法1. .dtsi 头文件2. 设备节点3. 标准属性3.1 compatible 属性3.2 model 属性3.3 status 属性3.4 #address-cells 和#size-cells 属性3.5 reg 属性 ...... 一、什么是设备树 设备树(Device Tree),将这个词分…

为什么不建议企业用薪资系统来跟踪项目时间?

身处在一个每分钟都很重要的世界里,企业必须勤于管理时间和工资。 虽然使用薪资系统进行时间跟踪似乎是一个实用的解决方案,但这种方法可能导致许多问题。 本文将讨论专用的时间跟踪软件对任何组织都必不可少的原因,以及依靠薪资系统进行时…

推特、微博对手Threads软件的下载、注册、使用最新超详细教程

经过马斯克不断折腾,推特面临用户大量流失的风险,尤其近期限制推文阅读量,更是导致大量用户出走。 于是乎,Meta公司7月6日正式发布对标推特的新社交平台 Threads,当前Threads只能在 iOS、Android 平台上安装 APP 使用&…

【JAVA】爱心代码--java特供(可直接复制,亲测有效)

个人主页:【😊个人主页】 文章目录 前言爱心的数学原理爱心代码基本版本带二种 前言 回看过去我发现我的第一篇博客竟然是一篇关于C语言爱心代码的博客(真是个奇怪的开始),不过这么长时间过去了,我的编程语…

软件设计模式与体系结构-设计模式-行为型软件设计模式-策略模式

目录 四、策略模式类图代码实例使用策略模式对中国的十二属相(Chinese Zodiac)设计查询系统。策略模式与状态模式课程作业 四、策略模式 类图 代码 策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算…

【Django】Django框架使用指南

Django使用指南 作者简介:嗨~博主目前是长安大学软件工程专硕在读📘,喜欢钻研一些自己感兴趣的计算机技术,求关注😉! 框架简介:Django是一个基于Python语言的开源Web应用框架,采用 M…

路径规划算法:基于学生心理学优化的路径规划算法- 附代码

路径规划算法:基于学生心理学优化的路径规划算法- 附代码 文章目录 路径规划算法:基于学生心理学优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要:本文主要介绍利用智能…

Django框架-11

聚合查询 1.聚合函数 使用aggregate()过滤器调用聚合函数。聚合函数包括:Avg 平均,Count 数量,Max 最大,Min 最 小,Sum 求和,被定义在django.db.models中。 例:查询图书的总阅读量。 from mo…

数据结构错题集 第七章 查找

7.2 124 等比 1(1-2^h)/(1-2) 2^h - 1 查找失败的最小次数相等吗? 13.A D 推一下公式 (M1)/2 平均查找长度 17.有序 就可二分查找 记住向下取整就是往右 13题就是个例子 向上取整就是往左 7.3 A错 不会分裂 不是平衡树 12。 C 黑高…

硬件基础——数字电路门电路

门电路与D触发器 一、与门 1.基本定义 与门又称 “与电路”、逻辑“积”、逻辑“与”电路,是执行“与”运算的基本逻辑门电路。有多个输入端,一个输出端。当所有的输入同时为高电平(逻辑1)时,输出才为高电平&#xf…

服务器进程查询

1. 查看当前正在运行的所有进程 ps -ef :查看当前所有正在运行的进程 UID:真实用户IDPID:进程的 IDPPID:父进程的 PIDCMD:运行当前进程的命令 2. 查看运行当前进程的指令 ps -aux | grep PIDPID表示你需要查询的进…

Linux 学习记录46(QT篇待完成)

Linux 学习记录46(QT篇) 本文目录 Linux 学习记录46(QT篇)一、建立QT项目工程二、1.2. 三、自动生成的文件介绍1. tempprj.pro2. mainwindow.h3. mainwindow.cpp4. main.cpp5. mainwindow.ui 四、常用类的介绍1. 信息调试类(1. qDebug(2. 输出当前界面尺寸(3. 设置当前界面尺寸…

第七章:YOLO v2网络详解

(目标检测篇)系列文章目录 第一章:R-CNN网络详解 第二章:Fast R-CNN网络详解 第三章:Faster R-CNN网络详解 第四章:SSD网络详解 第五章:Mask R-CNN网络详解 第六章:YOLO v1网络详解 第七章:YOLO v2网络详解 第八章:YOLO v3网络详解 文章目录 系列文章目录技…

PYQT QWidget的方法介绍

https://img-blog.csdnimg.cn/bae4318f1a9342ff85c9e7d27652cf91.png

uniapp打包app,对接华为厂商,实现unipush离线消息推送

今天终于可以抽出点时间,来记录一下这几天心塞的心情。上周公司派过来一个活,说是使用uniapp制作一个app,同时要实现在线消息推送和离线消息推送,啥话没说就揽了下来。不过说实在的,从来没有开发过app,好歹…

【网络安全带你练爬虫-100练】第9练:post提交/提取json数据包

目录 一、目标1:post提交json数据包 二、目标2:接收json数据包 三、目标3:提取指定的键值 四、网络安全小圈子 一、目标1:post提交json数据包 (大家可以自己随便找一个,像一些登录过的网站刷新一下&am…

jenkins构建历史设置保留数量

jenkins默认保留构建历史所有,这样磁盘空间越来越小,设置保留个数。 进入job项目中-配置 勾选Discard old builds,设置保留天数和个数,可以只填保留个数。 应用保存job配置,并重新构建项目,重新构建完成后…

基于pyqt5+opencv实现16位tif影像转jpg

现在大部分图像软件都支持tiff影像的浏览,但都是仅限于8位的影像,对应CV16U类型的tiff影像并不支持(这需要专业的gis软件才可进行操作)。为了便捷操作,故此基于pyqt5opencv实现16位tif影像转jpg的软件。 本博文涉及基于…

OpenCV4通道的分离split(),通道的合并merge(),通道的混合mixChannels()

文章目录 1、通道的分离函数 split()函数原型&#xff1a;&#xff08;1&#xff09;函数原型一&#xff1a;用 Mat型数组 Mat mvbegin[3]存储分离后的图像&#xff1b;输入参数&#xff1a; &#xff08;2&#xff09;函数原型二&#xff1a;用 vector容器 vector <Mat>…