PA3 存储管理
通过之前的 PA2 实验,我们创造了一台能够进行各种复杂运算的机器,它的计算能力与当今世界所有计算机等价,仅仅是速度上有些差距。
为了实现更快的计算速度,本章我们引入 Cache;
为了使得程序和数据得到保护,引入了分段保护机制;
为了使多个程序之前互不干扰,又产生虚拟地址空间的概念。
PA3-1 Cache 的模拟
问题:主存访问速度慢
对于一个存储硬件,它的读写速度与总大小、总造价有一定关系。
DRAM 器件:读写速度一般,价格低,空间大
SRAM 器件:读写速度很快,价格高,空间小
之前的实验中,代码和数据都是存放在 DRAM 中,每次都要对 DRAM 器件进行读写,浪费了大量时间
解决:引入 Cache
引入 Cache(SRAM 器件):
空间小,用来存放一些经常性的频繁使用的代码和数据。
每次要读写数据,先看 Cache 中有没有,有的话直接对 Cache 进行操作,速度很快;没有的话再去读写主存。
Cache 利用的是程序访问的局部性:
- 时间局部性:访问过的数据、代码在一段时间内可能再次使用
- 空间局部性:访问过的数据、代码的相邻部分
细节:Cache 的构造
1、要实现 Cache,我们需要建立一个 cache 与主存的映射关系,有三种方式:
- 直接映射
- 全相联映射
- 组相联映射
2、映射到的位置已满,删去哪个?
- 先进先出
- 最近最少用
- 最不经常用
- 随机替换
3、写入操作时,Cache 与主存不一致怎么办?
- 全写法
- 回写法
4、写操作时 Cache 缺失怎么处理?
- 写分配法
- 非写分配法
主要就是以上四个问题。
每个问题中的每种方法各有优劣,根据需求选择。
本实验中选择:
组相连映射法、随机替换、全写法、非写分配法
总结过程
我们创建了一个与主存对应的 Cache 存储器。
要对某个地址进行读操作时,先看 Cache 中有没有这个地址对应的区块,若有,直接从该块中取出数据;若没有,再去主存中把对应区块搬入 Cache,再次读取 Cache 中该区块的数据。
要对某个地址进行写操作时,先看 Cache 中有没有这个地址对应的区块,若有,修改该区块中该地址的值,设置脏位,等待该区块被替换或者程序结束时再覆盖主存的值(此处是回写法);若没有,先修改主存对应的数据,再把主存中对应区块搬入 Cache。
本节任务
定义一个 Cache,并实现它的读写函数。
PA3-2 保护模式
问题
在 3-2 之前,我们所有的寻址都是程序直接用物理地址进行访问的,不管是代码、数据、堆栈,都混在一起。
现代计算机采用保护模式,把这些不同的区域分开,称为“分段机制”。
解决
把所有代码数据分成不同的段:
代码段(CS)、数据访问(DS)、堆栈访问(SS)、特殊类型访问(ES)…
每一个段都对应了一个段寄存器,里面存储了该段的首地址。
程序给出一个指令的地址,不再是物理地址,而是相对于指令(代码段)的偏移地址。
即:物理地址=段首地址+段内偏移量
总结过程
程序刚开始时,初始化全局段描述符:
里面存储了所有段的段选择符和段首地址。
对于程序内的每次寻址,我们都会根据段选择符在段表中找到对应段的段首地址,取出段首地址,加上段内偏移量,就得到了最终要访问的物理地址。
本节任务
1、实现 lgdt、ljmp、特殊 mov 指令。
2、实现段转换函数、装载段寄存器函数。
PA3-3 虚拟地址转换
问题:
1、程序员在编程的时候,不知道在实际的机器中哪些空间可以用,哪些不能用。
2、如果程序员编写时使用物理地址,那他编写的多个程序不能同时在一台机器上运行。
。。。
总之就是,为了减少麻烦,我们应该实现一种机制,使得程序员在编程时,无需在意程序具体的物理地址,而是假装自己的程序就从某一固定的内存位置开始,空间不限。
至于这个程序在机器上实际运行的内存空间,交给机器自行分配。
所以,我们要实现一个编程时的虚拟地址空间到机器实际运行的物理地址空间的映射。
解决:分页机制
此处涉及三个不同的地址的概念
逻辑地址、线性地址、物理地址
在 PA3-2 中,通过分段机制,我们实现了从逻辑地址到线性地址的转化,这里我们将通过分页机制,实现从线性地址到物理地址的转化。
分页机制:
从用虚拟存储技术的计算机中,每一个进程都拥有自己独立的虚拟地址空间,主存地址空间和虚拟地址空间都被分成大小相等的页面,虚拟页和物理页。
虚拟页与物理页之间存在一一对应的关系,这种映射关系由页表维护。
采用了两级页表方式。
32 位的线性地址由三个字段组成:10 位页目录索引、10 位页表索引、12 位页内偏移量
转换过程:
先根据控制寄存器 CR3 中给出的页目录表首地址,找到页目录表,由 10 位页目录索引找到对应的页目录项,根据页目录项中的 20 位基地址指出的页表首地址找到对应页表,再根据线性地址中间的页表索引找到页表中的页表项,最后将页表项中的 20 位基地址和线性地址中 12 位页内偏移量相加得到 32 位物理地址。
本节任务
1、实现 page_translate()函数。
2、修改 loader()函数完成用户进程空间的分配。
总结
PA3 实现了存储管理。
用户进程给出一个 32 位逻辑地址和一个 16 位段选择符,
先通过分段机制,在段表中找出对应的段首地址,加上逻辑地址就得到线性地址。
接着通过分页机制,根据线性地址高 10 位、中间 10 位,在页目录表中找到对应的页,在页中找到对应的页表项,在页表项中找出该页对应的物理页号,加上线性地址的低 12 位便得到最终的物理地址。
接着通过Cache 机制,在 Cache 中找出该物理地址对应的块,若命中,则读取、写入(全写或回写);若不命中,则把主存中的部分搬入 Cache,再读取,或先写入主存,再搬入 Cache。
至此,便实现了读写功能。