底软驱动 | Linux虚拟内存

为了更有效的管理内存并且少出错,现代操作系统提供了一种对主存的抽象概念,叫做虚拟内存(VM)。虚拟内存提供了三个重要的能力:

  • 1.它将主存(物理内存)看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保留活动区域,并且根据需要在磁盘和主存之间来回交换数据。通过这种方式它高效的使用了主存。
  • 2.他为每个进程提供了一致的地址空间,从而简化了内存的管理。
  • 3.它保护了每个进程的地址空间不被其他进程破坏。
    这篇文章里就带大家了解一下虚拟内存是怎样实现上面的三种重要的能力的。

寻址与地址空间

在实际介绍虚拟内存的功能与实现之前我们必须先来大致了解一下物理寻址,虚拟寻址,物理地址空间,虚拟地址空间的概念。

物理寻址与虚拟寻址

计算机系统的主存被组织成一个由M个连续的字节大小的单元组成的数组,每个字节都有一个唯一的物理地址。第一个字节物理地址为0,下一个字节为1,在下一个为2,以此类推。不带任何存储器抽象的直接使用物理地址的方式就称作物理寻址(Physical addressing)。

如上图展示了一个物理寻址的示例。该示例的上下文是一条加载指令,它读取从物理地址4处开始的4字节。当CPU执行这条加载指令时,会生成一个有效的物理地址,通过内存总线,把它传递给主存,主存取出物理地址4处开始的4字节,并将它返回给CPU。早期的PC使用的是物理寻址,现代也有部分具有特殊用途的计算机系统会采用这种寻址方式。但是这种寻址方式对于现代的多道程序设计系统却不适用。所以现代处理器使用的是一种称为虚拟寻址(Virtual addressing)的寻址方式,参见下图。

使用虚拟地址,CPU通过生成一个虚拟地址(Virtual Address,VA)来访问主存。这个虚拟地址在被送到内存之前先被转换为了适当的物理地址。将一个虚拟地址转换成物理地址的过程叫做地址翻译。该地址翻译由一个CPU芯片上的叫做内存管理单元(Memory Management Unit,MMU)的专用硬件完成。MMU利用存放在主存中的查询表来动态的翻译虚拟地址。这个查询表的内容由操作系统管理,并且整个地址翻译的过程是系统自己完成的,不需要应用程序员操作。

物理地址空间与虚拟地址空间

地址空间是一个非负整数地址的有序集合.
如:

{0,1,2,3,...}

如果地址空间中的整数是连续的,我们说它是一个线性地址空间(在这里我们假设地址空间总是连续的)。在一个带有虚拟内存的系统中,CPU从一个有N=pow(2,n)个地址的地址空间中生成虚拟地址,这个地址空间称为虚拟地址空间(virtual address space):

{0,1,2,...,N-1}

一个地址空间的大小是由表示最大地址所需要的位数来描述的。例如,一个包含N=2的n次方个地址的虚拟地址空间就叫做n位地址空间。

一个系统还有还有物理地址空间(physical address space),对应与系统中物理内存的M个字节:

{0,1,2,...,M-1}

在了解了物理地址空间和虚拟地址空间之后,我们现在应该要建立这样一个认知:每个数据对象可以有多个独立的地址,其中每个地址都选自不同的地址空间。这就是虚拟内存的基本思想。主存中的每字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。

好了,在了解了与内存有关的概念之后,我们开始进入到虚拟内存的功能与实现的讨论了。

作为缓存的实现

在文章开头我们描述虚拟内存提供的三个重要能力中提到的第一个能力就是:虚拟内存将主存看作是存储在磁盘上的地址空间的缓存。下面我们就详细的讨论一下,这是如何实现的。

什么是虚拟内存

在概念上来讲,虚拟内存被组织为由存放在磁盘上的N个连续的字节大小的单元组成的数组。每个字节都有唯一的虚拟地址,作为到数组的索引(现在我们知道了虚拟内存虽然叫做内存,但其实它并不在内存中,而是磁盘中的一块空间)。虚拟内存与物理内存之间构成了一个缓存系统。虚拟内存位于较低层,物理内存位于较高层。虚拟内存上的数据可以被缓存到主存中。VM系统将虚拟内存分割为称为虚拟页(Virtual Page)的大小固定的块,每个虚拟页的大小为P=pow(2,p)字节。类似的,物理内存被分割为物理页(Physical Page),大小也为P字节,物理页也被称为页帧。在虚拟内存与物理内存之间交换的单元并不是一个一个的字节,而正是这些P字节大小的块。

虚拟内存的状态

在任意的时候,位于虚拟内存中的虚拟页都位于以下三种状态之一:

  • 未分配的
    处于未分配状态的虚拟页是VM系统还未创建的页(严格来讲,他们此时并不能称之为虚拟页,它们是还未被纳入虚拟内存范围的磁盘块)。这些磁盘块没有任何数据与他们关联,因此它们不占用任何的磁盘空间。
  • 缓存的
    当前已缓存在物理内存中的已分配页。
  • 未缓存的
    当前未缓存在物理内存中的已分配页。

如上图展示了一个有8个虚拟页的小虚拟内存,虚拟页0,3还未分配,因此在磁盘上还不存在,虚拟页1,4,6被缓存在物理内存中,页面2,5,7已经被分配了,但是当前并未被缓存在主存中。

页表

同任何缓存系统一样,虚拟内存系统必须有某种方法来判定一个虚拟页面是否缓存在物理内存中。如果是,系统还必须确定这个虚拟页存放在哪个物理页面中。如果不命中,系统还必须确定这个虚拟页存放在磁盘的哪个位置,从而将该虚拟页面读入到物理内存中的某个物理页面中,如果物理内存中没有多余的物理页面,那么还将选择一个牺牲页将其移出(在这里移出的时候还要考虑该牺牲页是否已经更新,如果更新还要对应的更新虚拟页面里面的内容),从而腾出空间放置新读入的物理页面的数据。
上述的这些功能是由软硬件联合提供的,包括操作系统软件、MMU中的地址翻译硬件和一个存放在物理内存中的的叫做页表的数据结构。页表将虚拟页面映射到物理页面。每次地址翻译硬件将一个虚拟地址转换为物理地址的时候都要读取页表。

如下图展示了一个页表的基本基本组织结构:

页表就是一个由页表条目(Page Table Entry,PTE)组成的数组。虚拟地址空间中的每个页在页表的一个固定偏移量处都有一个PTE。为了简单起见,这里我们假设每个PTE是由一个有效位(valid bit)和一个n位地址字段组成的。有效位表明了该虚拟页面是否被缓存在物理内存中。如果设置了有效位,则地址字段存放的就是物理内存中相应的物理页面的起始地址;如果没有设置有效位,那么一个空地址表示这个虚拟页还未被分配,否则,这个地址指向该虚拟页面在磁盘上的起始位置。

页命中与缺页

还是以页表小节中的图片为例,考虑一下当CPU想要读包含在VP2中的虚拟内存中的一个字时会发生什么,由于VP2已经被缓存在物理内存中。地址翻译硬件将虚拟地址作为索引来定位PTE2,并且从物理内存中读取它(前面讲过页面位于物理内存中),检查其有效位,发现其已经设置了有效位,此时地址翻译硬件就知道该页面已经缓存在物理内存中,并且该有效位后面的bit表示的就是对应的物理页面的地址。上面描述的这个过程就是页命中的情形。

下面我们来考虑一下缓存不命中的情形(在虚拟内存的习惯说法中,缓存不命中称为缺页)。

如下是缺页之前我们的示例页表的状态:

CPU想要读包含在VP3中的虚拟内存中的数据,地址翻译硬件根据CPU给出的虚拟地址,定位到页表中的PTE3,然后在内存中读取PTE3的内容,检查发现有效位为0,这表明当前CPU请求的页面并没有白缓存到物理内存中,这时就发生了缺页异常。这个缺页异常会调用内核中的缺页异常处理程序,该程序会选择一个牺牲页,在次例中该牺牲页就是存放在PP3中的VP4。如果VP4已经被修改,那么内核就会把它赋值回磁盘。无论哪种情况,内核都会修改VP4中的页表条目,反映出VP4已经不再缓存在物理内存中这一事实。

接下来,内核从磁盘复制VP3到内存中的PP3,更新PTE3,此时我们的示例页表状态就成为上图这样子了。当异常处理程序返回时,它会重新启动导致缺页的指令,该指令会把导致缺页的虚拟地址重新发送到地址翻译硬件。但是现在VP3已经缓存在主存中了,那么页面也就能够由地址翻译硬件正常处理了。

在这里,我们有一个要注意的问题,在上面的叙述中,我们随机选择了一个页面作为牺牲页,但是在实际中为了获得更好的性能是不会随机选择的,这需要涉及到页面置换算法,我们将在后面的一篇文章中介绍。

原文链接

  • Linux虚拟内存

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

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

相关文章

去除重复数字

1083. 【基础】去除重复数字 [ 刷题2路4线 ] 时间限制: 1000MS 空间限制: 16MB 结果评判: 文本对比 正确/提交: 29 (21) / 45 官方标签: 数组 普及- 题目描述 给你N个数(n<=100),每个数都在&am…

openEuler 安装 podman 和 podman compose

在 openEuler 22.03 LTS SP4 中,你可以使用 dnf 包管理器来安装 Podman 和 Podman Compose。openEuler 默认使用 dnf 作为包管理器,所以这是安装软件的首选方式。 关于 openEuler 22.03 LTS SP4 下载地址: https://www.openeuler.org/zh/dow…

【256 Days】我的创作纪念日

目录 🌼01 机缘 🌼02 收获 🌼03 日常 🌼04 成就 🌼05 憧憬 最近收到官方来信, 突然发现,不知不觉间,距离发布的第一篇博客已过256天,这期间我经历了春秋招、毕业答辩…

JS【详解】ES6 模块规范 vs CommonJS 模块规范

每个 js 文件都是一个模块,默认采用的 CommonJS 模块规范 新兴的 ES6 模块 pk 传统的 CommonJS 模块 特性CommonJSES6 模块化导出exports 对象export 关键字导入require()函数import 关键字加载模式同步异步执行模式单例单例依赖关系静态动态树形摇晃不支持支持 加…

板级调试小助手(3)基于PYNQ的OLED视频显示

一、前言 在之前的文章中介绍了《板级调试小助手》的系统结构和DDS自定义外设的搭建。这篇文章主要介绍一下如何在PYNQ中驱动平时长剑的OLED12864显示屏,并显示BadApple(毕竟有屏幕的地方就要有BadApple)。效果如下: BadApple 本项…

Access denied for user ‘root‘@‘localhost‘ (using password: YES)解决办法

在Spring配置数据源时,当使用Spring容器加载druid.properties数据库连接池配置文件时,容易碰到create connection SQLException, url: jdbc:mysql://127.0.0.1:3306/mydbs, errorCode 1045, state 28000 java.sql.SQLException: Access denied for user …

破解YouTube限制:保姆级教程,轻松查看博主邮箱

近期YouTube取消了博主的邮箱展示,这造成了不小的轰动,给想要联系博主的粉丝和想要寻求网红合作的品牌都带来了极大的不便。但这难不倒万能的网友!最新发现,通过一串神秘代码可以在YouTube上查看到博主的邮箱,这里Nox聚…

Java进阶----多态

多态 什么是多态 在继承父类或者实现接口的基础上,允许同一类事物出现不同的状态 多态的前提:继承或实现的关系、方法的重写、父类(接口)引用指向子类(实现类)对象 表现形式 父类 变量 子类对象(); 接…

解决方案架构师系列 - AWS - AWS Amplify 服务功能介绍

探索AWS Amplify:构建高效、安全的云原生应用 AWS Amplify 是一个开源框架,由亚马逊网络服务(AWS)提供,旨在简化Web和移动应用程序的开发。它是一套完整的开发工具集合,帮助开发者快速地实现功能如身份验证…

CycleGAN深度学习项目

远程仓库 leftthomas/CycleGAN: A PyTorch implementation of CycleGAN based on ICCV 2017 paper "Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks" (github.com) 运行准备 Anaconda 安装需要的库 指令 pip install panda…

LPRNet 车牌识别部署 rk3588(pt-onnx-rknn)包含各个步骤完整代码

虽然车牌识别技术很成熟了,但完全没有接触过。一直想搞一下、整一下、试一下、折腾一下,工作之余找了一个简单的例子入个门。本博客简单记录一下 LPRNet 车牌识别部署 rk3588流程,训练参考 LPRNet 官方代码。 1、导出onnx   导出onnx很容易…

昇思25天打卡营-mindspore-ML- Day24-基于 MindSpore 实现 BERT 对话情绪识别

学习笔记:基于MindSpore实现BERT对话情绪识别 算法原理 BERT(Bidirectional Encoder Representations from Transformers)是由Google于2018年开发的一种预训练语言表示模型。BERT的核心原理是通过在大量文本上预训练深度双向表示&#xff0…

Win7电脑修改网卡配置连接千兆网络的方法

Win7电脑修改网卡配置连接千兆网络的方法 Realtek PCIe GBE Family Controller是千兆网卡,GBE的意思就是1Gbps网卡,也就是千兆网卡,翻译成中文就是瑞昱PCI-E总线千兆网络系列控制器。 目前有很多的电脑都是使用realtek网卡的,当时奇怪的是网卡连接到h3或者d-link千兆交换机…

探索老年综合评估实训室的功能与价值

一、引言 随着人口老龄化的加剧,老年健康问题日益受到关注。老年综合评估实训室作为专门为老年人健康服务而设立的场所,具有独特的功能和重要的价值。 二、老年综合评估实训室的功能 (一)健康评估功能 1、身体功能评估 通过专业设…

【postgresql】权限(Privileges)

权限(privileges)是决定用户或角色可以对数据库对象(如表、视图、序列和函数)执行哪些操作的许可。权限对于维护安全性和控制对数据的访问至关重要。 权限分类 在 PostgreSQL 中,权限分为以下几种: SELEC…

数据库基本查询(表的增删查改)

一、增加 1、添加信息 insert 语法 insert into table_name (列名) values (列数据1,列数据2,列数据3...) 若插入时主键或唯一键冲突就无法插入。 但如果我们就是要修改一列信息也可以用insert insert into table_name (列名) values (列数据1&am…

客户端通过服务器进行TCP通信(三)

一. 对TCP的基础讲解 服务端 1. 首先创建一个套接字,TCP是面向字节流的套接字,故需要使用SOCK_STREAM 2. 然后使用bind()函数将套接字与服务器地址关联(如果是在本地测试,直接将地址设置为217.0.0.1或者localhost,端口号为1000…

内存函数(C语言)

内存函数 以下函数的头文件:string.h 针对内存块进行处理的函数 memcpy 函数原型: void* memcpy(void* destination, const void* source, size_t num);目标空间地址 源空间地址num,被拷贝的字节个数 返回目标空间的起始地…

Python与自动化脚本编写

Python与自动化脚本编写 Python因其简洁的语法和强大的库支持,成为了自动化脚本编写的首选语言之一。在这篇文章中,我们将探索如何使用Python来编写自动化脚本,以简化日常任务。 一、Python自动化脚本的基础 1. Python在自动化中的优势 Pyth…

在 YAML 中的变量(使用 和 * 定义及引用变量)

在 YAML 文件中,使用 & 和 * 是一种常见的定义和引用变量的方式。也是最简单的方式 使用 & 定义变量 在 YAML 中,& 符号用于定义一个锚点(anchor),也就是一个命名的变量。这个变量可以在文件的其他地方被引用和复用。 例如: title: &sc test在这个例子中,t…