系统内存管理:虚拟内存、内存分段与分页、页表缓存TLB以及Linux内存管理

虚拟内存

虚拟内存是一种操作系统提供的机制,用于将每个进程分配的独立的虚拟地址空间映射到实际的物理内存地址空间上。通过使用虚拟内存,操作系统可以有效地解决多个应用程序直接操作物理内存可能引发的冲突问题。

在使用虚拟内存的情况下,每个进程都有自己的独立的虚拟地址空间,它们不能直接访问物理内存地址。当程序访问虚拟内存地址时,操作系统会进行地址转换,将虚拟地址映射到物理地址上,这样不同的进程运行时,写入的是不同的物理地址,避免了互相覆盖指针的问题。

虚拟内存的使用使得每个进程都可以拥有相同的虚拟地址空间,而不用担心与其他进程的地址冲突。操作系统负责管理虚拟地址和物理地址之间的映射关系,并在需要时进行地址转换。这样,进程可以以一种透明的方式访问内存,无需关心内存的实际物理位置。

image

通过虚拟内存机制,操作系统能够更好地管理系统内存资源,提供更高的安全性和稳定性。它可以为每个进程提供独立的地址空间,保护进程间的数据隔离,同时也可以有效地利用物理内存,将不常用的数据交换到磁盘上(交换区),以提供更大的可用内存空间。

内存分段

在分段机制下,虚拟地址由两部分组成:段选择子和段内偏移量。段选择子是一个索引,用于指定要访问的段的起始地址和长度。段内偏移量则表示在该段内的具体位置。

操作系统会维护一个段表,其中包含了每个段的起始地址和长度信息。当程序访问一个虚拟地址时,操作系统会通过段选择子从段表中找到对应的段描述符,然后根据段描述符中的信息计算出物理地址。

具体的映射过程如下:

  1. 程序访问虚拟地址,通过段选择子找到对应的段描述符。
  2. 根据段描述符中的基址和长度信息,计算出段的起始物理地址。
  3. 将段的起始物理地址与段内偏移量相加,得到最终的物理地址。

image

不过,需要注意的是,分段机制可能会导致内存碎片的问题,因为不同段的大小可能不同,导致一些碎片化的空间无法被利用。当不够内存分配的时候,会选择使用内存交换,先把一块正在使用的内存移到磁盘中,然后再移回来把中间留的内存缝隙全用上,虽然解决了内存碎片的问题,但是这个交换操作很慢,效率低,看下图示:

image

虚拟内存、分段和内存交换似乎解决了同时运行多个程序的问题,但仍存在性能瓶颈。由于硬盘访问速度较慢,每次内存交换都需要将大段连续的内存数据写入硬盘。因此,如果交换的是占用大量内存空间的程序,整个系统会变得卡顿。

为了解决内存分段的碎片和提高内存交换效率,引入了内存分页机制。

内存分页

内存分页是将整个虚拟和物理内存空间划分为固定大小的连续内存块,称为页(Page)。在Linux下,每一页的大小通常为4KB。虚拟地址与物理地址之间通过页表进行映射,页表存储在CPU的内存管理单元(MMU)中,从而CPU可以直接通过MMU找到实际访问的物理内存地址。

虚拟地址与物理地址之间通过页表来映射,如下图:

image

由于内存空间事先划分为固定大小的页,不会像分段机制那样产生碎片。当释放内存时,以页为单位进行释放,避免了无法利用的小内存块。

如果内存空间不足,操作系统会将其他正在运行的进程中的"最近未使用"的内存页面暂时存储到硬盘上,称为换出(Swap Out)。当需要时,再将页面加载回内存,称为换入(Swap In)。因此,每次写入硬盘的是少量的一页或几页,不会花费太多时间,从而提高了内存交换的效率。

image

简单分页

简单分页存在空间上的缺陷。在操作系统可以同时运行大量进程的情况下,页表会变得非常庞大。在32位环境下,虚拟地址空间为4GB,假设页的大小为4KB,就需要大约100万个页。每个页表项需要4字节来存储,所以整个4GB空间的映射需要4MB的内存来存储页表。

尽管4MB的页表看起来并不算太大,但要注意每个进程都有自己的虚拟地址空间,也就是说每个进程都有自己的页表。如果有100个进程,就需要400MB的内存来存储页表,这对于内存来说是相当大的开销,更不用说64位环境下了。

多级页表

要解决上述问题,我们可以采用一种叫做多级页表(Multi-Level Page Table)的解决方案。在之前我们已经了解到,在32位环境下,页大小为4KB的情况下,一个进程的页表需要存储100多万个页表项,每个项占用4字节的空间,因此一个页表需要4MB的内存空间。

为了节省内存空间,我们可以将单级页表进行分页,将一个页表(一级页表)分为1024个页表(二级页表),每个二级页表包含1024个页表项,形成二级分页结构。这样一级页表覆盖整个4GB的虚拟地址空间,而对于未使用的页表项,不会创建对应的二级页表,只在需要时才创建。如下图所示:

image

换个角度来看,大多数程序未使用到整个4GB的虚拟地址空间,因此部分页表项是空的,没有分配实际的内存空间。在物理内存紧张的情况下,操作系统会将最近一段时间未访问的页表换出到硬盘,从而释放物理内存。使用二级分页,一级页表只需要覆盖整个4GB的虚拟地址空间,而未使用的页表项不需要创建对应的二级页表。假设只有20%的一级页表项被使用,那么页表占用的内存空间只有0.804MB,相比于单级页表的4MB,内存节约非常巨大。

为什么不分级的页表无法实现这样的内存节约呢?从页表的性质来看,页表保存在内存中,其主要作用是将虚拟地址翻译为物理地址。如果在页表中找不到对应的页表项,计算机系统将无法正常工作。因此,页表必须覆盖整个虚拟地址空间。而不分级的页表需要100多万个页表项进行映射,而二级分页只需要1024个页表项(一级页表覆盖整个虚拟地址空间,二级页表在需要时创建)。

页表缓存TLB(Translation Lookaside Buffer)

TLB(Translation Lookaside Buffer)是一个位于CPU芯片中的缓存,用于存储程序中最常访问的页表项,以加快虚拟地址到物理地址的转换速度。多级页表虽然解决了空间上的问题,但是增加了转换的工序,导致时间上的开销。然而,由于程序的局部性原理,程序执行期间通常仅限于某一部分,访问的存储空间也局限于某个内存区域。因此,通过将最常访问的页表项存储到TLB这个硬件缓存中,可以更快地进行地址转换。

在CPU芯片中,内存管理单元(Memory Management Unit)芯片负责处理地址转换和TLB的访问与交互。当CPU进行寻址时,首先会查找TLB,如果找到了对应的页表项,就可以直接进行物理地址的访问,避免了继续查找常规页表的开销。

由于TLB中存储的是程序最常访问的几个页表项,所以TLB的命中率通常是很高的。这是因为程序执行过程中,访问的页表项相对固定。通过利用TLB,可以大大提高地址转换的速度,加快程序的执行效率。

Linux内存管理

image

Linux内存管理涉及逻辑地址和线性地址的转换。逻辑地址是程序使用的地址,而线性地址是通过段式内存管理映射的地址,也称为虚拟地址。

Linux的虚拟地址空间分为内核空间和用户空间两部分。32位系统中,内核空间占用1G,剩下的3G是用户空间;64位系统中,内核空间和用户空间都是128T,分别占据内存空间的最高和最低处。如下所示:

image

进程在用户态时只能访问用户空间内存,进入内核态后才能访问内核空间内存。虽然每个进程都有独立的虚拟内存,但虚拟内存中的内核地址关联的是相同的物理内存,这样进程切换到内核态后就可以方便地访问内核空间内存。

总结

虚拟内存是操作系统提供的一种机制,通过将每个进程分配的独立的虚拟地址空间映射到实际的物理内存地址空间上,解决了多个应用程序直接操作物理内存可能引发的冲突问题。虚拟内存的使用使得每个进程都可以拥有相同的虚拟地址空间,而不用担心与其他进程的地址冲突。通过虚拟内存机制,操作系统能够更好地管理系统内存资源,提供更高的安全性和稳定性。虚拟内存的实现方式有分段和分页,其中分页机制更为常用,采用多级页表的方式节约了内存空间。页表缓存TLB能够加快虚拟地址到物理地址的转换速度。Linux的内存管理涉及逻辑地址和线性地址的转换,将虚拟地址空间分为内核空间和用户空间,方便进程访问内核空间内存。


我是努力的小雨,一名 Java 服务端码农,潜心研究着 AI 技术的奥秘。我热爱技术交流与分享,对开源社区充满热情。同时也是一位掘金优秀作者、腾讯云内容共创官、阿里云专家博主、华为云云享专家。

💡 我将不吝分享我在技术道路上的个人探索与经验,希望能为你的学习与成长带来一些启发与帮助。

🌟 欢迎关注努力的小雨!🌟

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA打开持久层的代码很卡,关掉mybatis-plus的插件

不知道大家有没有遇到过打开 mapper 层的页面,然后要切换另外 java 文件的时候很卡,我遇到过卡了好几分钟的,那种继承了 mybatis-plus 的 mapper java文件或者 xml 文件都会,我后来把 mybatis 的插件关掉了,就不会了

LVS的12种调度算法详解

1.lvs调度算法类型 1.1静态方法 仅根据算法本身进行调度,不考虑RS的负载情况 1.2动态方法 主要根据每RS当前的负载状态及调度算法进行调度Overheadvalue较小的RS将被调度 1.1lvs静态调度算法 1.1.1RR(轮询算法): roundrobin 轮…

Haproxy状态页

HAProxy 的状态页是一个非常有用的监控和诊断工具,它提供了关于 HAProxy 服务器运行状态的详细信息,通过web界面,显示当前HAProxy的运行状态。 状态页通常包含以下关键信息: 前端(Frontend)和后端&#x…

从TiDB迁移到OceanBase的实践分享

本文来自OceanBase热心用户的分享 近期,我们计划将业务数据库从TiDB迁移到OceanBase,但面临的一个主要挑战是如何更平滑的完成这一迁移过程。经过研究,了解到OceanBase提供的OMS数据迁移工具能够支持从TiDB到OceanBase的迁移,并且…

js构造函数的prototype赋值总结

我们知道通过构造函数的prototype,可以生成让所有实例对象访问的通用属性和方法,下面通过代码来解释这个过程 function Person(name){this.name name; }Person.prototype.sex man我们定义了一个构造函数Person,然后给它的prototype添加了一个sex的属性,下面我们来看看Person…

OSPF进阶

一、LSA详解 Type:LSA的类型(1、2、3、4、5、7类) link-state-ID:链路状态表示符 ADV router:产生该LSA的路由器 age:老化时间 Metric:开销值,一般都为ADV router到达该路由的开…

人工智能,代跑通,预测模型,模型优化

人工智能,代跑通,预测模型,模型优化,增加模块,文章复现,python代做,预测,微调,融合,深度学习,机器学习程序代写,环境调试,…

STM32-门电路-储存器-寄存器-STM32f1-MCU-GPIO-总线-keil5-点led-寄存器编程

1、门电路 门电路组成简单加法器: 二进制对电路的影响: 0和1代表无和有; 以下图例,演示与门:左1右1输出1; 电平标准:使用不同的电压表示数字0和1; 高电平:1&#xff1…

攻防世界-web-ctf-upload

题目场景 查看源码 毫无有效的数据 官方WriteUp 本题需要利用文件上传漏洞点,通过绕过服务器的安全防护,达到getshell的目的 本题的主要考点为利用fastcgi的.user.ini特性进行任意命令执行 这里需要绕过的点如下 检查文件内容是否有php字符串 检查…

haproxy七层代理

目录 一、haproxy简介 二、haproxy实验 1.环境部署 2.haproxy的基本部署方法及负载均衡的实现 2.1安装软件 2.2haproxy的基本配置 3.haproxy的全局配置参数及日志分离 3.1多线程设定 3.2自定义日志 4.haproxy-proxies中的常用配置参数 4.1设置backup --- sorryserver…

卷积神经网络(李宏毅老师系列)

自学参考: 一文搞懂卷积神经网络(CNN)的原理 视频课 课件资料 笔记 一、引入 cnn设计灵感来自于生物学中的视觉系统,旨在模拟人类视觉处理的方式。常用场景:image classification 基本步骤: 把所有图片都先…

数据结构--第六天

--树 -树的基本概念 树结构通常用来储存逻辑关系为“一对多”的数据,例如: 上图的这些元素具有的就是 "一对多" 的逻辑关系,例如元素A同时和B、C、D有关系,元素D同时和A、H、I、J有关系等。 观察这些元素之间的逻辑关…

模拟经营之神:《北境之地》安卓手机游戏,免费分享

《北境之地》(Northgard)是一款以北欧神话为背景的即时战略游戏,由Shiro Games开发。玩家在游戏中扮演维京部落的领袖,目标是探索新大陆、建立据点、管理资源,并在严酷的冬季和敌人的威胁下生存下来 。 游戏特色包括&a…

gin路由

1主文件 package main import ("github.com/gin-gonic/gin""godade/user""net/http" ) func main() {router : gin.Default()router.GET("/", func(c *gin.Context) {c.String(http.StatusOK, "Hello World")})v1 : router…

如何避免项目发布后用户从浏览器WebPack中看到源码

打包前在config->index.js中设置productionSourceMap为false productionSourceMap: false,

C# AI鉴图宝 利用OCR技术对违规图片进行判别

目录 效果 项目 代码 下载 效果 项目 代码 using Aspose.Cells; using NLog; using OpenCvSharp; using OpenVINO.OCRService; using Sdcb.OpenVINO; using Sdcb.OpenVINO.PaddleOCR; using Sdcb.OpenVINO.PaddleOCR.Models; using System; using System.Collections.Conc…

Jmeter性能压测4000并发

性能测试的底层逻辑 程序为什么会有性能问题 用户操作 客户端(web/app/小程序)触发网络请求,服务器处理大量网络请求代码运行需要大量服务器资源(CPU、内存、网络、磁盘等等) 资源不是无限,硬件配置不是随…

使用Python发送PDD直播间弹幕(协议算法分析)

文章目录 1. 写在前面2. 接口分析3. 算法还原 【🏠作者主页】:吴秋霖 【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python…

日撸Java三百行(day19:字符串匹配)

目录 一、字符串的一些基础知识 二、代码实现 1.字符类的创建 2.字符类的遍历 3.字符串匹配 4.字符串截取 5.数据测试 6.完整的程序代码 总结 一、字符串的一些基础知识 字符串(String)是用一对双引号括起来的零个或多个字符组成的有限序列&am…

简单的docker学习 第13章 CI/CD与Jenkins(上)

第13章 CI/CD 与 Jenkins 13.1 平台登录页面 13.1.1 GitLab-8098-root 13.1.2 Jenkins-8080-zhangsan 13.1.3 SonarQube-9000-admin 13.1.4 harbor-80-admin 13.2 CI/CD 与 DevOps 13.2.1 CI/CD 简介 CI > Continuous Integration,持续集成。即将持续不断更新…