4.2虚拟存储器

简介: 计算机组成原理之虚拟存储器

一、引子

在这一小节中,我们要学习这一章的最后一个内容,就是虚拟存储器。

在操作系统那门课里,虚拟存储器这个部分,将花整整一个章节来介绍相关的东西。

但是计组这门课当中,虚拟存储器相关的这些概念和内容,在王道书里只有一两页的篇幅。所以这也说明,虚拟存储器这个部分的内容,其实重点还是要在操作系统学习。

因此在小结当中,我们只对这个部分的内容做一个简要的了解。

二、虚拟存储系统

(1)介绍

首先来看什么是虚拟存储系统

学到这个阶段,大家应该能够体会到,其实我们的计算机系统很多地方都是套一层套一层的。

比如刚开始我们本来以为把内存里的东西丢入 cache 就 OK 了,但是后来我们知道cache,它又分为了L1、L2、 L3 这样的多级cache。每一级的 cache 都是一层套娃。

这个小节我们要学习的虚拟存储系统又是更完美一层的套娃

之前我们说过微信。比如装在手机里边,总共是 1GB 的大小。当微信启动的时候,我们需要把微信这 1GB 的数据全部调入到内存,微信才可以开始正常的运行。

基于局部性原理,由于我们某一个时间段内只有可能用到微信的某一部分的代码或者数据,因此我们可以把内存里的某一部分数据给它复制一份到 cache 当中,用这样的方式来提升整体的性能。
image.png

现在大家思考一下,辅存里边存储的这 1 GB的微信数据,我们有必要直接全部把它放到内存里吗?

其实没有必要,同样还是因为局部性原理,比如大家在使用微信的时候,其实大部分时候可能也就是文字聊天,还有朋友圈这两个功能用得比较频繁,至少对我来说是这样。

所以当我在我自己的手机上启动微信的时候,大概率在很多情况下只用得到文字聊天和朋友圈相关的这些代码和数据。

所以至少对我来说,当我在启动微信的时候,视频聊天还有什么扫一扫,摇一摇,附近的人,这些东西大家应该都知道哈,这些东西相关的数据和内容对于我来说是没有必要调入到内存的。

所以这就意味着我们的一个应用程序,只需要把部分的内容调入到内存,就可以开始正常运行。因此,虽然这个地方内存只有 4 GB。 然而在这部手机上,我们可以同时运行微信,王者荣耀,微博,爱奇艺,抖音,各种各样的应用程序。所有的这些应用程序,如果把这些应用程序的总体积加起来,一定是大于 4GB 的。

然而我们只有 4GB 的内存可以同时运行这么多的应用程序。原因就是,所有的这些程序,所有的应用,我们只需要调入部分的数据,就可以正常的使用了。

至于暂时没有调入的部分,之后用到的时候再把它调进来就可以了。所以这就是所谓的虚拟存储系统
在我们用户看来,似乎手机它的内存已经远超过了 4GB 这样的大小,因为我们可以在手机上同时运行超过 4GB 大小的好多个应用程序。

然而在物理上,实际上手机只有 4GB 的大小,所以我们自己感觉到内存好像很大。

这只是虚拟的一种现象,只是我们感受到了而已。

因此,这样的存储系统就是虚拟存储系统。用户感知到的存储系统的容量要比它物理上的真实容量要更大,这是操作系统虚拟性的一个体现。

总之,虚拟存储系统其实有点类似于我们之前把内存的部分数据调入cache,只不过虚拟存储系统是我们把辅存里的部分数据调入到了内存,只不过是层次不一样而已,原理和之前都是类似的,都是基于局部性原理
image.png

讲到这个地方,大家可以思考一下,我们平时打游戏的时候,比如什么吃鸡,王者荣耀,这两个游戏应该是现在玩的比较多的。
大家在某一局游戏开打之前,是不是会有一个 loading 界面,有一个进度条正在加载资源巴拉巴拉的,它会给你这样的一个提示。

在 loading 界面的背后,其实正在做的就是把游戏地图之类的这些相关的数据资源从外存(辅存)调入内存,是在做这样的一个事情。

讲到这儿,大家就能够知道为什么 loading 界面会比较慢了,因为之前我们说过,辅存的读写速度是比较慢的,所以我们要加载什么游戏地图相关的数据,这些需要等比较长的时间。

所以其实虚拟存储系统我们平时随时都在使用。
image.png


(2)分页

现在问题来了,我们的微信有 1GB 的大小,我们刚才说可以把微信的某一部分数据先把它调入内存。

那怎么界定一部分的大小?

关于这个问题,我们可以继续套娃。

比如我们可以结合上一小节学习的页式存储器,就是把一个程序的数据进行分页,每一页为一个单位,把它放到主存的各个位置。
image.png

如果我们要用分页的这种思想来实现虚拟存储器,就意味着我们的程序它总共有 4KB 的大小,它会被分为四个大小相同的块,或者四个大小相同的页面。

对于虚拟存储器来说,我们只需要把这个程序当前用得到的那些数据先把它调入主存,就可以让它正常地开始运行了。

假设此时程序暂时只用得到 0 号页面和 1 号页面这两个页面的数据,我们就可以先把这两个页面的数据放到主存相应的位置。
2 号和 3 号页面的内容我们可以先让它放在外存里边,这样可以保证我们主存的利用率会更充分。

由于此时有的逻辑页面没有被调入主存,因此我们需要对上个小节提到的页表进行一些改造。


1.有效位

上小节我们提到的页表只是完成了逻辑页号到主存块号之间的一个映射。

但是现在由于某一些逻辑页面它有可能还在外存当中,没有被调入内存,因此我们需要增加一个所谓的有效位,当有效位为 1 的时候,表示这个页面已经被调入主存了。而当有效位为 0 的时候,比如对于 2 和 3 这两个页面,这两个页面有效位为0,意思就是这两个页面相关的数据此时还被我们放在辅存当中,还没有调入内存。

如果接下来我们需要使用到 2 号页面相关的数据,我们只需要把这个页面的数据从辅存调入主存就可以。

2.辅存块号

我们应该到辅存的什么位置去找?关于这个问题,我们可以增加一个所谓的辅存块号,或者叫外存块号来解决。

我们之前把 cache 还有主存都进行了分块,每一块的大小都是1KB,那么我们也可以把辅存的整个存储空间 128GB 也分成 1KB1KB 的大小。

所以这也就意味着对于 2 号和 3 号页面,我们也可以把它存放在外存的某一块的位置,外存也把它进行分块。

所以我们只需要根据外存块号就可以找到每一个页面的数据,它存放在外存的什么位置。

因此,如果之后我们需要用到 2 号页面,我们只需要到外存块号为 c 的地方去把这一整个外存块调入主存就可以。

所以这就是外存块号还有有效位的作用。


3.访问位

接下来看访问位有什么作用。

访问位是为了实现页面替换算法才增加的一个属性,有点类似于我们 cache 的替换,我们之前说过, cache 的容量比主存的容量更小,所以我们把主存的某些数据放到 cache 当中,很容易导致 cache 被填满。当 cache 被填满之后,我们就需要选择某一些 cache 块把它换出去。

现在页面替换算法其实和 cache 替换是一样的,只不过页面替换算法解决的是主存和辅存之间的一个替换。原理类似,辅存很大,主存很小,主存里边就注定只能保存辅存中某一些数据的副本。

而由于主存的容量比辅存更小,因此主存很容易被填满。当主存被填满之后,我们应该把哪些主存块里存放的页面给替换出去?这就是页面替换算法要解决的问题。

而页面替换算法也可以使用我们之前提到的LRU、 FIFO来解决这一类的替换算法,来决定到底要把哪一个给替换出去。

具体的我们在操作系统再详细讲解,这儿就暂且不展开。

现在我们回到访问位。

刚才我们说访问位是为了实现页面替换算法,我们可以这么做,比如可以用访问位来记录最近一段时间内每一个页面它被访问了多少次,结合每一个页面被访问的次数,我们是不是就可以实现 LFU。也当我们需要淘汰一个页面的时候,可以优先地淘汰被访问的总次数比较少的页面。

这就是访问位的作用。

4.脏位

最后还有一个脏位,这个脏位其实和 cache 块的脏位也是一样的意思。

对于 cache 来说,如果我们修改了 cache 当中某一块的数据,当这一个 cache 块的数据需要被淘汰,需要被替换的时候,由于 cache 块它的脏位为1,也就是它被修改过,因此我们淘汰 cache 块的时候,需要把 cache 块的数据写回主存。

现在对于虚拟存储器来说也是一样的,我们对主存里的某一些块的内容更改了之后,当这个块被淘汰的时候,由于这一块的数据已经被更改了,也就是它的脏位是1,因此我们需要把这一块的内容给它写回辅存。

这就是脏位的作用。

所以主存与辅存它们之间的这些页面管理策略和主存与 cache 之间的很多管理策略完全都是相通的,只不过是在不同的存储层次而已

三、页式虚拟存储器

了解了这些之后,大家就能够看懂王道书里给的图了。
image.png

这个地方给出了页式虚拟存储器的页表的结构。

有效位就是它有没有被调入主存。如果有效位为0,说明页面相关的数据没有被调入储存。因此你看这个地方,它画了一个箭头,把它指向了磁盘的某一个磁盘块。
image.png

对于已经调入主存的这些页面来说,它这地方画的箭头就是指向了这所谓的物理存储器。其实这个东西就是主存,每一个页面是放在了主存的某一个位置。

没有调入主存的页面是放在了磁盘的某一个块的位置。

和上一小节介绍的类似,当 CPU 在执行一个指令的时候,指令里边指明的肯定是一个逻辑地址逻辑地址是由逻辑页号和页内地址组成的,而逻辑页号又可以称为虚页号

所以 CPU 可以根据虚页号去查询页表,然后找到虚页号所对应的页表项

根据页表项的信息,就可以知道当前要访问的逻辑页面有没有被调入到主存。

如果没有调入主存,只需要把磁盘里存储的这一页的数据调入主存的某一位置就可以。

只要调入主存,我们就可以根据主存块号还有页内地址来拼接出最终需要访问的一个物理地址

至于如何把某一些外存块的数据调入内存,这个问题是操作系统来管理的,因此这部分的内容重点还是在操作系统那门课里进行研究。

四、存储器的层次化结构

了解了虚拟存储器之后,我们再回头看我们这一章刚开始讲到的存储器的层次化结构
image.png

辅存的容量非常大,主存的容量一般大, cache 的容量非常小。

我们只能把辅存里的部分数据调入主存,主存里的部分数据调入cache,主存和辅存之间的数据交换主要是由操作系统来负责完成的,而主存和 cache 之间的数据交换是由硬件,也就是由 CPU 自动完成的。

因此我们在计算机组成原理这门课里重点关注的还是 cache 和主存,就是这两个存储层次之间的数据交换。

主存和辅存实现了虚拟存储系统,解决了主存的容量不够的问题。

而 cache 和主存之间主要解决的是 CPU 与主存之间速度不匹配的问题。

这是计算机里边最主要的三个存储层次。

五、段式虚拟存储器

之前我们认识了什么是虚拟存储器,并且介绍了什么是页式虚拟存储器,就是我们可以把一个程序拆分成一个一个大小相同的页面,以页面为单位来决定需要把哪些页面先调入主存,这是页式虚拟存储器。
image.png

还有一种常见的虚拟存储器实现方式,叫做段式虚拟存储器。我们可以把一个程序,把它按照功能模块拆分成若干个段。
比如 4 KB的程序,我们把它拆分成三个段,每一个段的大小不一样。如下:
image.png

比如 0 号段是你自己写的代码, 1 号段是你调用的其他人的库函数, 2 号段是用来存放你自己定义的某一些变量。

接下来操作系统会以为单位来决定到底先把哪些段给调入主存。

段式虚拟存储器相比于页式虚拟存储器来说,我们把程序拆分成多个部分,拆分的原则是以功能模块作为依据来进行拆分的,每一个功能模块的大小可能会不一样。

因此这也就导致了每一个段,我们拆出来的每一个段,它的段长度是不一样的。

而如果采用页式虚拟存储器,我们给程序进行拆分的时候,并不会管这些数据之间有没有什么逻辑关系,只需要把它拆分成大小相等的页面就可以。


对于段式虚拟存储器来说,逻辑地址,也就是虚拟地址的结构就会出现一些变化。
image.png

第一个部分是段号,第二个部分是段内地址。

而对于页式虚拟存储器,第一个部分是页号,第二个部分是页内地址。

由于每一个分段它的长度是不一样的,所以我们需要给段表增加段长这样的一项。

另外,当我们采用段式虚拟存储器的时候,主存不会再进行分页分块,我们给的每一个主存地址对应一个字节,我们的每一个段可以存放到主存的任何一个地方。

为了完成虚拟地址到物理地址的转换,因此我们需要记录每一个段它在主存当中的起始地址是多少。这就是所谓的段首址

具体断式存储是如何实现的?它比起页式存储来有哪些好处和不足之处?这一点我们会在操作系统那门课里进行更详细的探索。这儿

我们简单的过一下就可以。

反正无论是分段还是分页, CPU 一定都需要把虚拟地址,把它通过一系列的手段转换成实际的物理地址。

接下来, CPU 会根据物理地址去 cache 或者储存当中访问相应的数据。

六、段页式虚拟存储器

最后再来简要的提一提段页式虚拟存储器

顾名思义,所谓的段页式,就是先把一个程序进行分段,然后再进行分页。

你看,又开始套娃,刚才我们已经说过,这个程序被我们分为了这样的三个段。
image.png

比如第一个段,它的大小是 2. 5 kB,如果采用段页式存储,我们又可以把这个段进一步地拆分成三个页面。

因为每一个页面的大小是1KB,所以总共需要把它拆分成三个页面,才可以装得下2.5 KB 这样的大小。

所以这就是段页式存储。

以上就是这小节的全部内容,具体的在操作系统讲解喽。

相关文章
|
3月前
|
缓存 资源调度 BI
《零成本提升QClaw运行速度,这5招就够了》
本文针对QClaw随使用时长增加逐渐卡顿的普遍痛点,打破“卡顿必升级硬件”的常见误区,指出问题根源在于默认配置不合理与错误使用习惯。作者通过三周系统性实测,总结出五个零成本、立竿见影的性能优化技巧,涵盖模型分层加载、动态上下文裁剪、任务批量合并、本地缓存分级管理与后台进程资源隔离。这些技巧无需额外投入,可让QClaw运行速度直接翻倍,且适用于所有本地运行的智能体工具,为技术从业者提供了可直接落地的通用性能优化方案。
572 9
|
3月前
|
弹性计算 人工智能 测试技术
2026年最新阿里云服务器租用收费价格:ECS、轻量、GPU云服务器活动价格参考
2026年阿里云服务器租用价格更新,轻量应用服务器2核2G年付仅38元,2核4G月付9.9元、年付199元,性价比极高,适合个人开发者及小微企业。ECS经济型e实例与通用算力型u1实例分别以99元/年、199元/年提供稳定服务,满足企业级需求。GPU云服务器提供T4、V100等型号折扣,支持AI训练与高性能计算。用户可根据需求、身份及预算,通过活动选购合适服务器,并关注续费价格、购买限制,善用免费试用资源以优化成本。
973 1
|
10月前
|
存储 人工智能 资源调度
MCP协议深度集成:生产级研究助手架构蓝图
本文详解基于LangGraph与MCP协议构建研究助手的技术方案,涵盖双服务器集成、状态化智能体设计与用户元命令控制,助你掌握生产级代理系统开发要点。
918 1
|
9月前
|
安全 Java 决策智能
Spring Boot自动装配
Spring Boot自动装配基于“约定优于配置”理念,通过条件化配置与Starters机制,智能推断并加载所需组件,大幅简化开发流程。它实现配置自动化,提升效率,降低维护成本,支持自定义扩展,推动微服务快速构建,是Java生态中开发范式的革新之作。(238字)
|
8月前
|
云安全 监控 安全
阿里云渠道商:云安全中心怎么进行防勒索实战?
阿里云云安全中心构建多层防勒索体系,融合日志追踪、行为分析与威胁库动态更新,实现分钟级攻击检测与响应。通过诱饵捕获、自动化备份及智能规则迭代,助力企业快速预警、阻断并恢复勒索攻击,打造弹性安全防线。(238字)
|
自然语言处理 Linux API
Linux man命令英文天书?别怕,揭秘一招,让你瞬间解锁中文秘籍,轻松驾驭Linux命令海洋!
【8月更文挑战第22天】Linux以其开源特性、强大功能和灵活性深受技术爱好者喜爱。但对于新手而言,面对系统中众多命令及其英文man页面常感吃力。本文介绍如何将man命令内容转化为中文,降低学习门槛。首先强调man命令的重要性——它是获取Linux命令帮助的核心工具。接着,提出一种利用ccze工具结合在线翻译服务的方法,尽管需要额外处理,但对于寻求即时中文帮助的用户仍具价值。最后推荐使用tldr这样的工具,它提供简化的多语言man页面,方便快捷地查看中文命令说明,使学习过程更加顺畅。
487 1
|
数据采集 自然语言处理 JavaScript
Playwright多语言生态:跨Python/Java/.NET的统一采集方案
随着数据采集需求的增加,传统爬虫工具如Selenium、Jsoup等因语言割裂、JS渲染困难及代理兼容性差等问题,难以满足现代网站抓取需求。微软推出的Playwright框架,凭借多语言支持(Python/Java/.NET/Node.js)、统一API接口和优异的JS兼容性,解决了跨语言协作、动态页面解析和身份伪装等痛点。其性能优于Selenium与Puppeteer,在学术数据库(如Scopus)抓取中表现出色。行业应用广泛,涵盖高校科研、大型数据公司及AI初创团队,助力构建高效稳定的爬虫系统。
909 2
Playwright多语言生态:跨Python/Java/.NET的统一采集方案
|
人工智能 关系型数据库 分布式数据库
阿里云PolarDB重磅发布云原生与Data+AI新特性,打造智能时代数据引擎
阿里云PolarDB重磅发布云原生与Data+AI新特性,打造智能时代数据引擎
1044 0
|
缓存 NoSQL Java
自研一套高性能API网关,你都需要做什么?
本文介绍了自研API网关的基本组成和架构,涵盖网关的作用、请求生命周期、功能点详解(如插件和规则)、技术选型(Java、Netty、Nacos)及设计要点。通过异步化设计、缓存、串行化优化、提高吞吐量和合理配置工作线程,确保高性能与稳定性。最后,提供了网关的架构图,帮助读者全面理解其构建过程。
801 0
|
Java 数据处理 Spring
Spring Cloud OpenFeign 超时与重试
今天给大家分享的是 `feign` 的超时与重试配置。
1353 0
Spring Cloud OpenFeign 超时与重试