计算机组成原理:简单页表和多级页表(虚拟内存的映射)
我们的指令和数据,都必须先加载到内存,才会被CPU拿去执行。但是程序并不能直接访问到物理内存。从这里可以知道,程序是怎么装载到内存中执行的。
我们的内存需要被分成固定大小的页(Page),然后才通过虚拟内存地址到物理内存地址的地址转换,才能到达实际存放数据的物理内存位置。而我们的程序看到的内存地址,都是虚拟内存地址。
那么,这些虚拟内存地址究竟是怎么转换成物理地址的呢?
内存保护的主要目的是防止某个进程去访问不是操作系统配置给它的寻址空间
简答页表
想要把虚拟内存地址,映射到物理内存地址,最直观的方法,就是来键一张映射表。这个映射表,能够实现虚拟内存里面的页,到物理内存里面的页的一一映射。这会映射表,在计算机里面,就叫做页表(page table)
页表这个地址转换的办法,会把一个内存地址分成页号(Directory)和偏移量(Offset)两个部分。以一个32位的内存地址为例:
- 前面的高位,就是内存地址的页号,后面的低位,就是内存地址的偏移量
- 做地址转换的页表,只需要保留虚拟内存地址的页号和物理内存地址的页号之间的映射关系就可以了。
- 同一个页里面的内存,在物理层面是连续的
总结一下,对于一个内存地址转换,其实就是这样三个步骤:
- 把虚拟内存地址,切分成页号和偏移量的组合;
- 从页表里面,查询出虚拟页号,对应的物理页号;
- 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址
看起来这个逻辑似乎很简单,很容易理解,不过问题马上就来了
- 32 位的内存地址空间,页表一共需要记录 2 2 0 2^20 220 个到物理页号的映射关系。这个存储关系,就好比一个 2 2 0 2^20 220 大小的数组。一个页号是完整的 32 位的4 字节(Byte),这样一个页表就需要 4MB 的空间。听起来 4MB 的空间好像还不大啊,毕竟我们现在的内存至少也有 4GB,服务器上有个几十 GB 的内存和很正常。
- 但是,这个空间又不止占用一份。我们每一个进程,都有属于自己独立的虚拟内存地址空间。这也意味着,每一个进程都需要这样一个页表。不管我们这个进程,是个本身只有几 KB 大小的程序,还是需要几 GB 的内存空间,都需要这样一个页表
这还只是 32 位的内存地址空间,现在大家用的内存,多半已经超过了 4GB,也已经用上了 64 位的计算机和操作系统。这样的话,用上面这个数组的数据结构来保存页面,内存占用就更大了。那么,我们有没有什么更好的解决办法呢?
多级页表
仔细想一想,我们其实没有必要存下这 2^20 个物理页表啊。大部分进程所占用的内存是有限的,需要的页也自然是有限的,我们只需要去存那些用到的页之间的映射关系就好了。
也就引入了多级页表(Multi-Level Page Table)。那为什么不用哈希表呢?
(哈希表有哈希冲突,而且顺序乱,不符合局部性原理)
我们先来看一看,一个进程的内存地址空间是怎么分配的。在整个进程的内存地址空间,通常是“两头实,中间空”。在程序运行的时候,内存地址从顶部往下,不断分配占用的栈的空间。而堆的空间,内存地址则是从底部往上,是不断分配占用的。
所以,在一个实际的程序地址里面,虚拟内存占用的地址空间,通常是两端连续的空间。而不是完全随机的内存地址。而多级页表,就特别适合这样的内存地址分布。
我们以一个4级的多级页表为例,来看一下,同样一个虚拟内存地址,偏移量的部分和上面简单页表一样不变,但是原先的页号部分,我们把它拆成四段,从高到低,分成 4 级到1 级这样 4 个页表索引。
对应的,一个进程会有一个 4 级页表。我们先通过 4 级页表索引,找到 4 级页表里面对应的条目(Entry)。这个条目里存放的是一张 3 级页表所在的位置。4 级页面里面的每一个条目,都对应着一张 3 级页表,所以我们可能有多张 3 级页表。
找到对应这张 3 级页表之后,我们用 3 级索引去找到对应的 3 级索引的条目。3 级索引的条目再会指向一个 2 级页表。同样的,2 级页表里我们可以用 2 级索引指向一个 1 级页表。
而最后一层的 1 级页表里面的条目,对应的数据内容就是物理页号了。在拿到了物理页号之后,我们同样可以用“页号 + 偏移量”的方式,来获取最终的物理内存地址。
我们可能有很多张 1 级页表、2 级页表,乃至 3 级页表。但是,因为实际的虚拟内存空间通常是连续的,我们很可能只需要很少的 2 级页表,甚至只需要 1 张 3 级页表就够了
事实上,多级页表就像一个多叉树的数据结构,所以我们常常称它为页表树。因为虚拟内存地址分布的连续性,树的第一层节点的指针,很多就是空的,也就是不需要有对应的子树了。所谓不需要子树,其实就是不需要对应的 2 级、3 级的页表。找到最终的物理页号,就好像通过一个特定的访问路径,走到树最底层的叶子节点。
以这样的分成 4 级的多级页表来看,每一级如果都用 5 个比特表示。那么每一张某 1 级的页表,只需要 2^5=32 个条目。如果每个条目还是 4 个字节,那么一共需要 128 个字节。而一个 1 级索引表,对应 32 个 4KiB 的也就是 16KB 的大小。一个填满的 2 级索引表,对应的就是 32 个 1 级索引表,也就是 512KB 的大小。
我们可以一起来测算一下,一个进程如果占用了 1MB 的内存空间,分成了 2 个 512KB 的连续空间。那么,它一共需要 2 个独立的、填满的 2 级索引表,也就意味着 64 个 1 级索引表,2 个独立的 3 级索引表,1 个 4 级索引表。一共需要 69 个索引表,每个 128 字节,大概就是 9KB 的空间。比起 4MB 来说,只有差不多 1/500。
不过,多级页表虽然节约了我们的存储空间,却带来了时间上的开销,所以它其实是一个“时间换空间”的策略。原本我们进行一次地址转换,只需要访问一次就能找到物理内存地址。但是,用了4级页表,我们就需要访问4次内存,才能找到物理页号了。
问题是:内存访问其实比Cache要慢很多。我们本来只是要做一个简单的物理地址转换,现在却要多了访问多次内存。对于这个时间性能的损失,有什么好的办法吗?
总结
- 多级页表就像一棵树,因为一个进程的内存地址相对集中和连续,所以采用这种页表树的方式,可以大大节省页所需要的空间。而因为每一个进程都需要一个独立的页表,这个空间的节省是非常客观的
- 在优化页表的过程中,我们可以观察到,数组这样紧凑的数据结构,以及树这样稀疏的数据结构,在时间复杂度和空间复杂度的差异。另外,纯粹理论软件的数据结构和硬件的设计也是高度相关的。
计算机组成原理:简单页表和多级页表(虚拟内存的映射)相关推荐
- 【操作系统/计组】页面大小 与 页表项 ( 二级页表 、多级页表 )
[操作系统/计组]页面大小 与 页表项 结论1(一级页表) 结论2(二级.多级页表) 例题 首先,不论一级页表还是多级页表: 页面大小 = 2^(页内地址位数) 页号有多少,页就有多少个 用于存放页的 ...
- 计算机组成原理简单模型机实验,计算机组成原理简单模型机实验.doc
计算机组成原理简单模型机实验 实验四 简单模型机实验 1.1实验目的 1)将微程序控制器模块通过总线同运算器模块.存储器模块联机,组成一台模型计算机: 2)用微程序控制器控制模型机数据通路: 3)通过 ...
- 计算机组成原理简单计算机设计,计算机组成原理课设.doc
文档介绍: 课程设计任务书 学院 信息学院 专业 计算机科学与技术 学生姓名 学号 设计题目 多寄存器算术减法\右移位\输入输出\转移指令实验计算机设计(第5组) 内容及要求: 利用EL-JY-II型 ...
- 计算机组成原理简单模型机实验,CPU 与简单模型机设计实验
计算机科学与技术系 实 验 报 告 专业名称 计算机科学与技术 课程名称 计算机组成原理 项目名称 CPU 与简单模型机设计实验 班 级 学 号 姓 名 同组人员 无 实验日期 2016.6 一.实验 ...
- 计算机组成原理简单模型机实验,计算机组成原理简单模型机实验
实验四 简单模型机实验 1.1实验目的 1)将微程序控制器模块通过总线同运算器模块.存储器模块联机,组成一台模型计算机: 2)用微程序控制器控制模型机数据通路: 3)通过CPU运行5条机器指令组成的简 ...
- 计算机组成原理简单选择题,计算机组成原理选择题及答案.doc
1. 冯·诺依曼计算机中指令和数据均以二进制形式存放在存储器中,CPU区分它们的依据是( ) A. 指令操作码的译码结果B. 指令和数据的寻址方式C. 指令周期的不同阶段D. 指令和数据所在的存储单元 ...
- 【操作系统基础】页表 快表 多级页表
文章目录 页表 页面的大小 页表特性 PTBR--寄存器 快表 TLB 命中率 基于页表的保护与共享 多级页表 页表 为什么说分页的逻辑地址是一维的地址: 从下图我们可以看出,把一个程序分为等大的页面 ...
- 18.多级页表与快表
[README] 1.本文内容总结自 B站 <操作系统-哈工大李治军老师>,内容非常棒,墙裂推荐: 2.操作系统内存管理:分页机制+多级页表+快表来实现: [0]分页的问题 1)分页的问题 ...
- 虚拟内存,页表,快表,多级页表,倒排页表
虚拟内存 尽管基址寄存器和界限寄存器可以用于创建地址空间的抽象,还有另一个问题需要解决:管理软件的膨胀(bloatware).虽然存储器容量增长快速,但是软件大小的增长更快.需要运行的程序往往大到内存 ...
最新文章
- 饥荒海难机器人怎么用_饥荒海难机器人作用详解 机器人有什么用
- 从0开始学习GitHub系列之「向GitHub 提交代码」
- 接口持续集成环境搭建
- C语言 指针在函数传参中的使用
- 【♻️markdown之一次编写,到处使用♻️】markdown文件转word
- NOIP2018退役记
- Chrome让人失望,是时候转到Firefox或Edge?
- 深入理解 JVM Class文件格式(二)
- pytorch 查看参数是否被训练 require_grad()
- Python学习笔记:排列与组合
- docker容器内没有yum命令_为什么不建议把数据库部署在Docker容器内?
- Silverlight5 RC调用Win32API
- 三星 NAND 存储器新厂施工顺遂,上半年有望如期投产
- C++类库Pugixml与rapidxml性能评测
- 1-7 华为HCNA认证eNSP基础A
- dispatch js实现_dva.js(and design)实现dispatch的回调函数
- docker在centos7中run时遇到的坑?
- 【Python/爬虫】爬取网易云音乐评论区热评(练习)
- android撕衣服案例解析
- 读懂DeFi四大金融原语的演变:流动性、杠杆、风险和套利