602 KiB
存储系统
存储器概念
存储部件概念
- 存储元:由电路控制的单个存储部件。
- 存储单元:由同一个电路控制的一组同时读写的存储部件集合,一般为一行存储元,一行存储元的个数就代表一次存储的字长。
- 存储体:由多个存储体构成的,多个电路控制的存储集合。
- 存储字:存储单元通电后由电信号表示可以读写的一个存储单元信息集合。存储字的位数就是存储字长,单位是bit。
- 高电平有效:接收到高电平时代表该电路是工作的,接收到低电平时代表该电路是关闭的。
- 低电平有效:接收到低电平时代表该电路是工作的,接收到高电平时代表该电路是关闭的。若代表线路或开关的字母上有横杠代表低电平有效。否则一般都是高电平有效。
- 译码器:
- 由于同时对多个存储单元进行读写操作时,数据可能发生混乱。所以就需要用对应数据的所在地址来区分不同的数据。
- 假如使用$n$位地址码,有一种方法是用1代表当前读取的逻辑地址,即00..01就代表1,00..02就代表2,这时候每一位就对应一位,就只能表示$n$个存储单元。
- 为了提升效率表示更多地址,所以需要译码器将电平信号转换为逻辑信号,将普通的01信号变为逻辑的二进制信号,从而能表示的地址就变成了$2^n$个。
- 译码器的输入口为$A$、$B$、$N$等$n$个,若高电平有效,则输出口为$Y_i$,若低电平有效,则输出口为$\overline{Y_i}$,其中$i=2^n-1$。
- 若高电平有效,则译码器的图接口无论输入口还是输出口都是横杠;若是低电平有效,则译码器的图接口输入口是横杠,输出口是横杠并在靠近译码器方框的末端加上空心圈。
- 译码器具有使能端,类似于控制芯片的片选线,用于控制译码器的启动和关闭,若图上没有圈或字母上没有横杠则代表高电平有效,输入1就能开始工作。实际上会有多个使能端,只有高电平有效的使能端输入1,同时低电平有效的使能端输入0才能启动,只要输入有一个异常都不会工作。
- 输入口为2输出口为4的译码器就是24译码器,输入口为3输出口为8的译码器就是38译码器
- 所以总容量=存储单元个数×存储字长。
主存结构
- 存储矩阵:由大量相同位存储单元阵列。
- 译码驱动:将来自地址总线的地址信号翻译成对应存储单元的选通信号,该信号在读写电路的配合下未完成对被选中的单元的读写操作。
- 译码器:将MAR输入的地址进行译码,选择选中的存储单元地址。
- 驱动器:根据译码器提供的地址,通过驱动器获取对应存储单元。
- 读写电路:包括读出放大器和写入电路,用来完成读写操作。根据控制电路的读或写操作要求,将MDR传入的数据写入存储体中,或将存储体中的数据读出到MDR中。
- MAR:地址寄存器,保存从地址总线输入的地址。
- MDR:数据寄存器,保存读出或写入的数据。
- 地址线$A_i$:用来输入CPU要访问的主存地址,是单向的,位数与CPU芯片容量相关,一般与MAR位数相等。直接连接MAR。多少个存储单元就多少根地址线
- 数据线$D_i$:用来输入输出数据,是双向的,位数与读入写入数据位数相关。直接连接MDR。存储字长多少位就多少根数据线。
- 地址线和数据线位数同时决定的内存大小,假如地址线有$N$根,数据线有$M$根,则芯片容量为$2^N\times M$B。地址线位数代表存储体的行,数据线代表存储体的列。
- 片选线:是整个存储芯片的开关,用来确定哪一个存储芯片被选中,可用于容量扩充。
- $\overline{CS}$:芯片选择信号,低电平有效。
- $\overline{CE}$:芯片使能信号,低电平有效。
- 读写控制线,决定芯片进行读写操作:
- 如果是一根就用$\overline{WE}$表示,低电平写,高电平读。如果是$WE$则反之。
- 如果是两根,则$\overline{OE}$低电平表示允许读,$\overline{WE}$低电平表示允许写。如果是$OE$和$WE$则反之。
主存分配
- 指按照不同的长度切分存储单元。
- 若字长为4B,总容量为1KB,则按字节寻址是1K个单元,每个单元1B;按字寻址是256个单元,每个单元4B;按半字寻址是512个单元,每个单元2B;按双字寻址是128个单元,每个单元8B。
存储器的分类
- 作用:
- 高速缓冲存储器cache。
- 主存储器:
- 主存。
- 内存。
- 辅助存储器:
- 辅存。
- 外存。
- 存储介质:
- 磁表面存储器:
- 磁盘。
- 磁带。
- 磁芯存储器。
- 光存储器:光盘。
- 半导体存储器。
- 磁表面存储器:
- 存取方式:
- 随机存取:
- RAM:随机存取存储器:
- DRAM。
- SRAM。
- ROM:只读存储器。
- RAM:随机存取存储器:
- 串行访问:
- 直接存取:磁盘。
- 顺序存取:磁带。
- 随机存取:
- 信息可保存性:
- 断电后信息是否消失:
- 易失性:RAM。
- 非易失性:磁带、ROM。
- 破坏性,存取是否影响存储内存:
- 破坏性读出:DRAM。
- 非破坏性读出。
- 断电后信息是否消失:
存储器性能指标
- 存储容量:存储字数×字长(如1M×8位)。
- 单位成本:每位价格=总成本/总容量。
- 存储速度:数据传输率=数据的宽度/存储周期:
- 存储周期=存取时间+恢复时间。
- 存取时间(Ta):存取时间是指从启动一次存储器操作到完成该操作所经历的时间,分为读出时间和写入时间。
- 存取周期(Tm):存取周期又称为读写周期或访问周期。它是指存储器进行一次完整的读写操作所需的金部时间,即连续两次独立地访问存储器操作(读或写操作)之间所需的最小时间间隔。
- 主存带宽(Bm):主存带宽又称数据传输率,表示每秒从主存进出信息的最大数量,单位为字/秒、字节/秒(B/s)或位/秒(b/s)。
存储器层次化结构
- CPU。
- Cache:为了解决CPU的高速与主存之间的低速速度不匹配的问题,由硬件自动完成。
- 主存。
- 辅存:为了解决主存容量不足的问题,由硬件和操作系统共同完成。
- 虚拟存储系统。
半导体随机存储器
SRAM和DRAM
随机存取存储器RAM分为SRAM和DRAM,即静态与动态。
SRAM和DRAM的区别
| 类型 | SRAM | DRAM |
|---|---|---|
| 存储信息 | 双稳态触发器,分为0态和1态 | 电容,充电是1,否则为0 |
| 破坏性读出 | 非;读只用查看触发器状态;写只用改变触发器状态 | 是;读需要连接电容,检测电流变化,电流随着电路连通而溜走;写需要给电容充放电 |
| 需要刷新 | 不要,能保持两种稳定的状态 | 需要,因为电容上的电荷只能维持2ms |
| 送行列地址 | 同时送,因为地址分为行地址和列地址一同发送 | 分两次送,行地址和列地址分开,所以地址线可以复用,线路减少一半 |
| 运行速度 | 快 | 慢 |
| 集成度 | 低,6个逻辑元件构成 | 高,1个或3个逻辑元件构成 |
| 发热量 | 大 | 小 |
| 存储成本 | 高,常用于Cache | 低,常用于主存 |
DRAM的刷新
- 刷新周期:一般为2ms。
- 刷新单元数:以行为单元,每次刷新一行存储单元。
- 如果译码器有$n$位,则可以寻址$2^n$个,也就需要$2^n$与存储单元连接的线路,很难实现。
- 将地址拆分为行列地址(DRAM行、列地址等长)。
- 第一种需要$2^n$根线,而第二种需要$2^{\frac{n}{2}+1}$根线。
- 刷新方式:硬件支持,读出一行的信息后重新写入,占用一个读写周期。
- 刷新时刻:假设DRAM内部结构排列为128×128的形式,读写周期为0.5μs,所以2ms一共4000个周期
- 分散刷新:每读取完一行数据就刷新一次。在每1μs中前0.5用于读写,后0.5用于刷新该行。
- 集中刷新:有一段时间专门刷新,但是这时候就无法访问存储器,称为访存死区。因为有128行,所以一共需要专门刷新64μs,则前面正常读写需要3872个周期,读写时间为1936μs。
- 异步刷新:隔一段时间刷新一次,一共要刷新所有的行,而如果将刷新设置在不需要访存的译码时间可以加大利用效率。因为每隔2ms要刷新128次,所以平均2ms/128=15.6μs一次,每15.6μs中有0.5μs的死时间。
RAM的读写周期
- 读周期:
- 从给出有效地址开始,到读出所选中单元的内容并在外部数据总线上稳定地出现所需的时间,称为读出时间($t_A$)。从数据稳定到数据有效之间存在一个时间缝隙,因为数据线上的信号速度是不一样的,所。
- 地址片选信号$\overline{CS}$必须保持到数据稳定输出,$t_{CO}$为片选的保持时间,即发出片选信号的从地址有效到地址失效的时间,在读周期中$\overline{WE}$为高电平。
- 读周期与读出时间是两个不同的概念,读周期时间($t_{RC}$)表示存储芯片进行两次连续读操做时必须间隔的时间,因为里面存在要等待数据稳定才能开始读的等待时间等其他时间,所以必然大于等于读出时间。
- 写周期:
- 要实现写操作,要求片选信号$\overline{CS}$和写命令信号$\overline{WE}$必须都为低电平。
- 为使数据总线上的信息能够可靠地写入存储器,要求$\overline{CS}$信号与$\overline{WE}$信号相“与”的宽度至少为$t_{WC}$。
- 为了保证在地址变化期间不会发生错误写入而破坏存储器的内容,$\overline{WE}$信号在地址变化期间必须为高电平。
- 为了保证有效数据的可靠写入,地址有效的时间至少应为$t_{WC}=t_{AW}+t_W+t_{WR}$。其中$t_{AW}$和$t_{WR}$为写入前和写入后必须的间隔时间,$t_W$为写入的时间。
- 为了保证在$\overline{WE}$和$\overline{CS}$变为无效前能把数据可靠地写入,要求写入的数据必须在$t_DW$以前在数据总线上已经稳定。
ROM
只读存储器ROM即使断电也能保存数据,主存不能直接与CPU相连,所以一定会出现ROM来完成这个工作。
ROM写入速度不如RAM,所以一般还是用来保存信息而不用于大量的写。
ROM的分类
- 掩膜式只读存储器(MROM):存储内容由半导体制造厂按用户提出的要求在芯片的生产过程中直接写入,无法修改。
- 一次可编程只读存储器(PROM):存储内容由用户用专门的设备(编程器)一次性写入,之后无法修改。
- 可擦除可编程只读存储器(EPROM):修改次数有限,写入时间长:
- 紫外线擦除(UVEPROM)。
- 电擦除(EEPROM)。
- 闪速存储器(Flash Memory):如U盘,写入速度快。
- 固态硬盘(Solid State Drives):控制单元+FLASH芯片。
主存与CPU连接
连接原理
- MDR和MAR虽然为寄存器,但是现在一般集成在CPU上。
- 数据总线直接连接在MDR上,可以写入也可以读出,是一个双向的。
- 地址总线直接连接在MAR上,将CPU的地址要求交给主存,是一个单向的。
- 控制总线向主存发送控制类型,如读写要求,是一个单向的。
主存容量扩展
为了获取更多的容量,所以需要对主存容量进行扩展。
- 位扩展法:
- CPU的数据线数与存储芯片位数不一定相等,用多个存储器件对数据位数进行扩展(一次性输入输出数据数量)。
- 地址总线和片选线都是并联的,数据总线连接在每一块芯片上。
- 因为需要拓展位,所以一次性需要处理所有芯片的数据,从而需要对芯片同时进行片选线同步信号,所以所有芯片的$CS$都可以连接在一起。
- 每个芯片各输入输出一部分数据。
- 字扩展法:
- 增加存储器中字的数量(数据的地址大小即能保存的数据的数量),而位数不变,字扩展将芯片的地址线、数据线、读写控制线相应并联,与位扩展法连接方式一样。
- 但是如果每个芯片同时输入输出输数据则CPU无法区分到底是哪个芯片存储的数据,所以不能再将片选线连在一起同时控制。
- 需要用片选信号区分个芯片地址范围,即将每个芯片的片选线依次连接在CPU的地址线接口上,因为不能同时工作,所以片选线信号$CS$或$\overline{CS}$不会同时为1或0,而片选线的信号连接在CPU的地址线接口上就相当于将片选线的信号也作为芯片存储地址。
- 如一个CPU一共有16个地址线接口$A_0$到$A_{15}$与8个数据接口$D_0$到$D_7$以及一个读写控制线$WE$,现在有两个8K×8位的存储芯片,首先按位扩展时将两个芯片的地址线$A_0$到$A_{12}$全部串联接到CPU的$A_0$到$A_{12}$接口上,芯片读写线$WE$串联接到CPU的$WE$接口上,数据线$D_0$到$D_7$借到CPU数据接口$D_0$到$D_7$上。如果这时CPU发出地址信号0 0000 0000 0000,就无法识别这个地址是第一块还是第二位芯片的第一位。此时CPU的$A_{13}$到$A_{15}$三个接口还是空的,将芯片1的选片线$CS$接到CPU的$A_{13}$上,芯片2的选片线$CS$接到CPU的$A_{14}$上,此时CPU的地址信号会变成15位。因为$CS$指高电平1有效,$A_{13}$为1代表选择芯片1,$A_{14}$为1代表选择芯片2,所以010 0000 0000 0000代表选择芯片1,100 0000 0000 0000带包选择芯片2。若高位为110或000则冲突而浪费位数。
- 这种选择芯片的方法就是片选,这种片选方法就是线选法:
- 线选法:当某地址线信息为”0’时,就选中与之对应的存储芯片,只能一位有效。优点:不需要地址译码器,线路简单。缺点:地址空间不连续,选片的地址线必须分时为低电平(否则不能工作),不能充分利用系统的存储器空间,造成地址资源的费。
- 译码片选法:由于译码器可以将$n$位映射到$2^n$位,所以通过地址译码芯片产生片选信号。如线选法如果三位编码只能选择三个芯片,而译码片选法三位编码可以选择八个芯片,即三位二进制编码。优点:地址空间可连续,可以增加逻辑设计。缺点:电路逻辑复杂。
- 每个芯片各存储一部分数据。
- 字位同时扩展法:即增加存储字的数量又增加存储字长。各芯片连接地址线的方式相同,但是连接数据线的方式不同,需要通过片选信号$\overline{CS}$或采用译码器设计连接到对应芯片。
例题 设CPU有16根地址线,8根数据线,并用$\overline{MREQ}$作为访存控制信号(低电平有效),用$\overline{WR}$作为读/写控制信号(高电平为读,低电平为写)。现有下列存储芯片:1K×4位RAM,4K×8位RAM,8K×8位RAM,2K×8位ROM,4K×8位ROM,8K×8位ROM及74LS138译码器和各种门电路。画出CPU与存储器的连接图,要求:
1)主存地址空间分配:6000H67FFH为系统程序区;6800H6BFFH为用户程序区。
2)合理选用上述存储芯片,说明各选几片?
3)详细画出存储芯片的片选逻辑图。
系统程序区需要使用ROM,用户程序区需用RAM。
第一步根据地址线、数据线,选择存储芯片。
对于系统程序区,CPU数据线有八根,所以存储器的位数应该为八位。CPU从6000H到67FFH之间一共有800H个地址,即为2K个地址。所以就应该选择2K×8位的ROM。对于用户程序区,6800H到6BFFH一共有400H,即1K,需要使用1K×8位的RAM,但是这时候没有,为了不造成浪费使用两块1K×4位的RAM进行位扩展。
然后是地址分配,2K的ROM的地址线应该是11根地址线,1K的RAM地址线应该是10根地址线。而CPU一共有16根地址线,所以可以分配给RAM和ROM。ROM地址6000H到67FFH是二进制0110 0000 0000 0000到0110 0111 1111 1111,所以选择最低11位进行连接。RAM地址6800H到6BFFH是二进制0110 1000 0000 0000到0110 1011 1111 1111,所以选择最低10位进行连接。
然后进行选片信号,即什么时候用RAM什么时候用ROM,其中74LS138译码器是用三位选八位,所以对于ROM和RAM地址,选择低位占用更多的地址的从低位开始的能区分ROM和RAM地址的三位,即第三位到第六位的100和101。即地址是100就是ROM,101就是RAM。所以CBA为100时选择$Y_4$,为101时选择$Y_5$。
4LS138还有三个使能端,一个高电平有效$G_1$,两个低电平有效$\overline{G_{2A}}$、$\overline{G_{2B}}$。这时地址前面还有两位01,可以作为译码器的使能端,第一位0代表低电平有效的使能端,第二位的1代表高电平有效的使能端$\overline{G_1}$。余下的一个低电平有效的使能端正好对应低电平有效的$\overline{MREQ}$作为访存控制信号决定译码器是否工作。
其中为什么两个RAM还需要一个&操作,是因为RAM的地址不仅仅需要第三位到第六位是101,还需要第七位是0,所以必须同时保证$A_{10}=0$和$\overline{Y_5}=0$,然后从&这个操作传回两个RAM得到选片信号。
同时只有RAM需要直接连接读写控制线$\overline{WR}$,而ROM读写控制线是静态的,所以可以直接连接到地面就可以了。
另外还有一个问题,就是ROM和CPU之间的箭头的问题,如果ROM是在此无法写入的,就是单向的箭头,从ROM到CPU,但是这里是双向的,说明这里指可以写入ROM。
双端口RAM和多模块存储器
由于CPU与主存相连,而CPU速度增长是指数级别的,而主存容量增长是线性的,所以双方速度不匹配,所以需要更快的访问速度。一方面可以更高性能存储器,一方面使用高速缓冲存储器。
存取周期由存取时间和恢复时间构成,所以缩短存取周期的方法一种是空间上并行,一种是时间上并行。
双端口RAM
左右有两个独立的端口,所以有两对独立的数据线、地址线、控制线。
两个端口对同一主存操作有以下4种情况:
- 两个端口不同时对同一地址单元存取数据。
- 两个端口同时对同一地址单元读出数据。
- 两个端口同时对同一地址单元写入数据。会产生写入错误
- 两个端口同时对同一地址单元,一个写入数据,另一个读出数据。会产生读出错误。
所以只用设置一个“忙”的标志位,若CPU发现该端口为忙,则等待一段时间再进行访问。
多模块存储器
- 单体多字存储器:普通存储器是每行为一个存储单元,而对于单体多字存储器来说,每个存储单元存储$m$个字,若总线宽度也为$m$个字,则一次并行就能读出$m$个字。但是只用数据和指令是连续存放在内存的才能这样操作。
- 多体并行存储器:每个模块都有相同的容量和存取速度,以及独立的读写控制电路、地址寄存器和数据寄存器。地址分为体号和体内地址两个部分
- 高位交叉编址的多体存储器:高位是体号,低位是体内地址。按列,模块里先编址,一个个模块进行分配。只是相当于扩容而已,对于速度没有改变。若连续取$n$个存储字,每次访问需要$T$的时间,则耗时$nT$。
- 低位交叉编址的多体存储器:低位是体号,高位是体内地址。按行,每一个单元先编址,一行行进行分配。由于每个存储体都是独立的,所以可以间隔一小段时间就能进行另一个的存储单元的访存而不用等待上一个单元的阶数。若连续取$n$个存储字,每次访问需要$T$的时间,启动间隔为$\tau$,则耗时$T+(n-1)\tau$。
例题 模块数为$m$,存储周期为$T$,字长为$W$,数据总线宽度为$W$,总线传输周期为$r$,连续存取$n$个字,求交叉存储器的带宽。(有$m$个存储体,存储周期为$T$,字长为$W$,每隔$r$时间启动下一个存储体,连续存取$n$个字,求交叉存储器的存取速率。)
连续存取$n$个字耗时为$T+(n-1)r$,但是需要$m\geqslant\dfrac{T}{r}$。因为如果模块数过少,则轮流到某一个存储体时这个存储体上一次的处理还没有完成就无法继续工作了。
所以带宽是$\dfrac{n\times W}{T+(n-1)r}$。
当$n$无限大时,带宽趋近于$\dfrac{W}{r}$,而单个存储体的带宽为$\dfrac{W}{T}$。
高速缓冲存储器
高速缓冲存储器基本概念
局部性原理
- 时间局部性:程序所访问的数据在相邻时间也可能访问到。
- 空间局部性:程序所访问的数据的周围数据也可能访问到。
高速缓冲存储器地址
- 主存储器的地址包括主存块号和块内地址,而Cache的地址包含缓冲块号和块内地址,两个块内地址都是一样长的且完全相同的。
- Cache还有一个标记,用来说明Cache块与主存块的关系,等于此块在主存中的块号。
工作流程
- CPU发出访问地址,从地址总线传输到Cache。
- 通过Cache主存地址映射变换结构在Cache中寻址对应数据。
- 如果命中就访问Cache并取出指令通过数据总线返回CPU。
- 如果不命中就访问主存取出信息通过数据总线返回CPU,并把这个信息存储在Cache中。
- 需要检测Cache是否已满,如果不满就直接将新的主存块调入Cache进行存储。
- 若已经满了则通过Cache替换结构执行替换算法腾出空位再调入。
命中与未命中
- 命中:主存块调入缓存,主存块与缓存块建立了对应关系,用标记记录与某缓存块建立了对应关系的主存块号。
- 未命中:主存块未调入缓存,主存块与缓存块未建立对应关系。
- 命中率:CPU欲访问的信息在Cache中的比率,设一个程序执行期间,Cache的总命中次数为$N_c$,访问主存的总次数为$N_m$,则命中率$H=\dfrac{N_c}{N_c+N_m}$。
- 命中率与Cache的容量与块长有关。一般每块可取4到8个字,块长取一个存取周期内从主存调出的信息长度。
- 相联存储器:并行比较标记,若有标记与当前将要访问的地址的标记相同,且有效位为1(表示当前存储单元存储数据),则命中;若标记不同,则直接替换。
高速缓冲存储器的效率
- 效率$e$与命中率有关,$e=\dfrac{\text{访问Cache的时间}}{\text{平均访问时间}}\times100%$。
- 假如Cache和主存是同时被访问的,设Cache命中率为$h$,访问Cache的时间为$t_c$,访问主存的时间为$t_m$,则$e=\dfrac{t_c}{h\times t_c+(1-h)\times t_m}\times100%$。当$h=0$时最小为$\dfrac{t_c}{t_m}$,当$h=1$时最大为$1$。
例题 假设cache的速度是主存的5倍,且Cache的命中率为95%,则采用Cache后,存储器性能提高多少(同时考虑:同时被访问Cache命中则中断访问主存;先访问Cache,若不命中再访问主存)?
设Cache的存取周期为$t$,则主存存储周期为$5t$。
若Cache和主存同时访问,命中时的访问时间就是$t$,则不命中时的访问时间就是访问主存的时间,也就是$5t$。
所以系统的平均访问时间为$t_a=0.95\times t+0.05\times5t=1.2t$。
所每个周期可存取的数据量为$D$,则存储系统的带宽就是$\dfrac{D}{1.2t}$,不采用Cache的带宽为$\dfrac{D}{5t}$,所以性能为原来的$\dfrac{D/1.2t}{D/5t}=\dfrac{5}{1.2}\approx4.17$,即提高了3.17倍。
若先访问Cache再访问主存,则命中时的访问时间就是$t$,则不命中时的访问时间就是访问主存的时间加上之前访问Cache消耗的$t$时间,总共为$6t$。
所以系统的平均访问时间为$t_a=0.95\times t+0.05\times6=1.25t$。
所以性能为原来的$\dfrac{5}{1.25}=4$倍,即提高了3倍。
地址映射
即将主存块调入Cache时应该将其放在哪里。
全相联映射
- 空位随意放,因为无法查看Cache里面每个存储块是否有数据,所以还需要一个有效位来表示里面是否有数据,将有效位放入标记项中。
- 但是这时候还是不知Cache里的每一个存储单元存的是主存的哪一块数据,所以还需要将主存的主存块号保存到标记项中,从而标记项数据的第1位代表Cache该单元是否存储数据即有效位,后面的位标识存储的是主存哪一块数据即主存字块标记。
- 因为是乱序存放,所以一定要把整个主存块号都放入标记项中。
- 地址=主存字块标记+字块内地址。
直接映射
- 对号入座,每一个主存块只能存放在唯一一个地方。
- 将主存储体按Cache存储体的长度划分为多个区,主存的每个区只能放在Cache的指定区域中。类似于模运算,将主存长度对Cache长度取模,地址相同余数的内存块放在同一个Cache块中。
- 这种映射方式也需要一位有效位标识是否有数据存储,也需要存入主存块号来标识存入的是哪个。
- 但是与全相连映射不同的是,因为直接映射是根据模运算来存储的,所以行与行之间是顺序的关系,所以主存块号不需要全部存入标记项,把能区分相同模结果的地址前一部分位的数据存入即可。
- 如Cache一共八行,则主存按八为一个周期存储,则如Cache的0号地址中一定存入主存块地址模八后余0的主存块,即地址都为为xx...000,同理1号地址存的都是xxx...001,所以可以保存主存地址减三的前面位数就足够了。这里的000和001等类似于Cache的组号。若Cache的行数为$2^n$,主存块号地址为$c$位,则标记项只用保存$c-n$位就可以了。
- 地址=主存字块标记+Cache字块标记+字块内地址。
- 优点:节省掉Cache字块标记,有效位存储的地址减少,实现难度降低。
- 缺点:空间利用率降低。
组相联映射
- 按号分组,组内随意放,结合了上面二者的优点。
- 也需要有效位,标记项也需要存入能区分数据块的部分地址。
- 将Cache的块分为几个组,主存不再按照Cache的行数进行模运算分组存入,而用Cache组的个数进行模运算,虽然这样能节省的位数变少,但是这样主存的某一块就可以在每一组内随机选一个存储,而不用只能存储在一个块内浪费其他块的空间。
- 地址=主存字块标记+Cache组地址+字块内地址。
- 当Cache组地址位数长度变为表示Cache行数的二进制位,那么组号和行号一样就变成了直接映射,若组地址位数为0,则Cache不分组都随机存储即变成了全相连映射。
替换算法
即Cache中存储满了如何处理。对于直接映射法来说,只有替换出前一个数据才能存入此时的数据,所以替换算法只针对全相联映射和组相联映射。
随机算法
即RAND算法:随机地确定替换的Cache块。它的实现比较简单,但没有依据程序访问的局部性原理,故可能命中率较低。但是这种方式基本上不会使用。
先进先出算法
即FIFO算法:选择最早调入的行进行替换。它比较容易实现,可以使用堆栈,但也没有依据程序访问的局部性原理,可能会把一些需要经常使用的程序块(如循环程序)也作为最早进入Cache的块替换掉。
近期最少使用算法
即LRU算法:依据程序访问的局部性原理选择近期内长久未访问过的存储行作为替换的行,平均命中率要比FIFO要高,是堆栈类算法。是一种局部性策略
LRU算法对每行设置一个计数器,Cache每命中一次,命中行计数器清0,而其他各行计数器均加1,需要替换时比较各特定行的计数值,将计数值最大的行换出。
最不经常使用算法
即LFU算法:将一段时间内被访问次数最少的存储行换出。每行也设置一个计数器,新行建立后从o开始计数,每访问一次,被访问的行计数器加1,需要替换时比较各特定行的计数值,将计数值最小的行换出。是一种全局性策略。
但是这种策略是无法实现的,因为计算机不可能全局考虑后面会出现什么,而只会考虑局部。
例题 设Cache由8个块构成,CPU依次访问的主存地址块号为:4,6,12,4,8,14,22,6,4,11,5,2(十进制),求:
1)假设地址映射方式为全相联映射,在采用FIFO、LRU、LFU替换算法时,分别求Cache命中次数。
使用全相连映射可以随机存放,为了方便按照顺序存放。
| Cache号\CPU访问主存序列 | 4 | 6 | 12 | 4 | 8 | 14 | 22 | 6 | 4 | 11 | 5 | 2 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 是否命中 | 否 | 否 | 否 | 是 | 否 | 否 | 否 | 是 | 是 | 否 | 否 | |
| 0 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | |
| 1 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | ||
| 2 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | |||
| 3 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | |||||
| 4 | 14 | 14 | 14 | 14 | 14 | 14 | ||||||
| 5 | 22 | 22 | 22 | 22 | 22 | |||||||
| 6 | 11 | 11 | ||||||||||
| 7 | 5 |
这时候还差一个2需要调入,但是没有多余的空间了。
FIFO:由于最先进入的是4,所以替换0号,将4变为2。
LRU:选择最近最久未使用的块,从第一位开始算,4目前来看出现了3次,6目前来看出现了2次,而后面的12、8、14、22、11、5都出现了1次,所以选最远的,是12号块,替换2号,将12变为2。
LFU:选择最不经常使用的,从全局来看,4出现了3次,6出现了2次,而12、8、14、22、11、5都出现了1次,又12最先进入,所以替换2号,将12变为2。
2)假设地址映射方式为直接映射,求Cache命中次数。
由于直接映射,所以直接对8取模,然后将对应相同余数的块替换掉就可以了。余数代表调入的Cache块号,商代表区分同一个Cache存储的不同主存块的标记。
| CPU访问主存序列 | 4 | 6 | 12 | 4 | 8 | 14 | 22 | 6 | 4 | 11 | 5 | 2 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 余数 | 4 | 6 | 4 | 4 | 0 | 6 | 6 | 6 | 4 | 3 | 5 | 2 |
| 商 | 0 | 0 | 1 | 0 | 1 | 2 | 2 | 0 | 0 | 1 | 0 | 0 |
根据余数和商计算得到对应处理结果:
| Cache号\CPU访问主存序列 | 4 | 6 | 12 | 4 | 8 | 14 | 22 | 6 | 4 | 11 | 5 | 2 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 是否命中 | 否 | 否 | 否 | 否 | 否 | 否 | 否 | 否 | 是 | 否 | 否 | 否 |
| 0 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | ||||
| 1 | ||||||||||||
| 2 | 2 | |||||||||||
| 3 | 11 | 11 | 11 | |||||||||
| 4 | 4 | 4 | 12 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
| 5 | 5 | 5 | ||||||||||
| 6 | 6 | 6 | 6 | 6 | 14 | 22 | 6 | 6 | 6 | 6 | 6 | |
| 7 |
3)假设地址映射方式为二路组相联映射,在采用FIFO、LRU、LFU替换算法时,分别求Cache命中次数。
二路组相联即两块未一组,所以一共四个组。这时候需要将块对4取模。余数代表调入的Cache组号,商代表区分同一个Cache组存储的不同主存块的标记。
| CPU访问主存序列 | 4 | 6 | 12 | 4 | 8 | 14 | 22 | 6 | 4 | 11 | 5 | 2 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 余数 | 0 | 2 | 0 | 0 | 0 | 2 | 2 | 2 | 0 | 3 | 1 | 2 |
| 商 | 1 | 1 | 3 | 1 | 2 | 3 | 5 | 1 | 1 | 2 | 1 | 0 |
若使用FIFO,每一组上面是队头,下面是队尾:
| 组号 | Cache号\CPU访问主存序列 | 4 | 6 | 12 | 4 | 8 | 14 | 22 | 6 | 4 | 11 | 5 | 2 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 是否命中 | 否 | 否 | 否 | 是 | 否 | 否 | 否 | 否 | 否 | 否 | 否 | 否 | |
| 0 | 0 | 4 | 4 | 12 | 12 | 12 | 12 | 8 | 8 | 8 | 8 | ||
| 1 | 4 | 4 | 12 | 12 | 8 | 8 | 8 | 8 | 4 | 4 | 4 | 4 | |
| 1 | 2 | ||||||||||||
| 3 | 5 | 5 | |||||||||||
| 2 | 4 | 6 | 14 | 22 | 22 | 22 | 22 | 6 | |||||
| 5 | 6 | 6 | 6 | 6 | 14 | 22 | 6 | 6 | 6 | 6 | 2 | ||
| 3 | 6 | ||||||||||||
| 7 | 11 | 11 | 11 |
命中一次。
若使用LRU,每一组上面是将要替换(最近不怎么用)的,下面是最近换入或替换的:
| 组号 | Cache号\CPU访问主存序列 | 4 | 6 | 12 | 4 | 8 | 14 | 22 | 6 | 4 | 11 | 5 | 2 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 是否命中 | 否 | 否 | 否 | 是 | 否 | 否 | 否 | 否 | 是 | 否 | 否 | 否 | |
| 0 | 0 | 4 | 12 | 4 | 4 | 4 | 4 | 8 | 8 | 8 | 8 | ||
| 1 | 4 | 4 | 12 | 4 | 8 | 8 | 8 | 8 | 4 | 4 | 4 | 4 | |
| 1 | 2 | ||||||||||||
| 3 | 5 | 5 | |||||||||||
| 2 | 4 | 6 | 14 | 22 | 22 | 22 | 22 | 6 | |||||
| 5 | 6 | 6 | 6 | 6 | 14 | 22 | 6 | 6 | 6 | 6 | 2 | ||
| 3 | 6 | ||||||||||||
| 7 | 11 | 11 | 11 |
命中两次。
若使用LFU,则需要考虑置换出的块后面是否还需要使用,将最近要用的保留下来:
| 组号 | Cache号\CPU访问主存序列 | 4 | 6 | 12 | 4 | 8 | 14 | 22 | 6 | 4 | 11 | 5 | 2 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 是否命中 | 否 | 否 | 否 | 是 | 否 | 否 | 否 | 是 | 是 | 否 | 否 | 否 | |
| 0 | 0 | 4 | 12 | 4 | 4 | 4 | 4 | 8 | 8 | 8 | 8 | ||
| 1 | 4 | 4 | 12 | 4 | 8 | 8 | 8 | 8 | 4 | 4 | 4 | 4 | |
| 1 | 2 | ||||||||||||
| 3 | 5 | 5 | |||||||||||
| 2 | 4 | 14 | 6 | 6 | 6 | 6 | 6 | 6 | |||||
| 5 | 6 | 6 | 6 | 6 | 6 | 22 | 22 | 22 | 22 | 22 | 2 | ||
| 3 | 6 | ||||||||||||
| 7 | 11 | 11 | 11 |
命中三次。
写策略
即Cache中内容修改后如何让主存于Cache修改的保持一致。分为当前命中和当前不命中的情况:
- 命中:全写法、写回法。
- 不命中:写分配法、非写分配法。
全写法
也称为写直通法或write-through:当CPU对Cache写命中时,必须把数据同时写入Cache和主存。
由于写Cache要远快于写主存,所以一般使用写缓冲(write buffer)暂存写入的数据,但是如果写的速度过快可能会出现溢出。
写回法
也称为write-back:当CPU对Cache写命中时,只修改Cache的内容,而不立即写入主存,只有当此块被换出时才写回主存(全部改完了再写回主存)。
需要在信息位中使用一个类似于有效位的10表示位,名为脏位,0代表未修改,1代表修改。
写分配法
也称为write-allocate:把主存中的块调入Cache,在Cache中修改。一般搭配写回法使用,即写完Cache之后再把Cache的值覆盖在主存的数据上,所以也需要设置一个脏位。
非写分配法
也称为not-write-allocate:只写入主存,不调入Cache。一般搭配全写法使用。
多级Cache
使用两级Cache,在与CPU直接连接的第一层Cache使用全写法,在与主存直接连接的第二层Cache使用写回法。
例题 设主存地址空间大小为1KB,按字节编址,Cache由8个块构成,每个Cache块大小为16B,CPU依次访问以下地址:0001001110、1001110010、0001001111、0011000010、0101001000、1011110010、1111010000、0011001001(十进制为78、626、79、194、328、754、976、201),求:
1)假设地址映射方式为全相联映射,在采用FIFO、LRU、LFU替换算法时,分别求Cache命中次数。
由于使用全相联映射,所以地址分为主存字块标记和字块内地址两个部分。
因为主存地址空间大小为1KB,且按字节编址,即一共可以标识1K个数据,则主存地址一共有10位二进制位。
又每个Cache块大小位16B,所以需要4位标识块,即4位的字块内地址。所以需要10-4=6位主存字块标记来区分主存块。
所以开始访问地址,按6+4分开,并设置8个有效位。并注意,如果调入一个地址,则回调入这个主存字块标记所标识的全部地址到Cache中,即若CPU访问xx...xxx,则会全部调入xx...000到xx...111的全部数据。
按题目所说,前面主存字块标记为4位(每个Cache块16位),所以16就进1,即按十进制来理解,十进制地址0到15在Cache的0中,16到31在Cache的1中等等。
第一个是000100 1110,所以Cache的0号块调入000100 0000到000100 1111的数据,并将有效位设置为1,标记为00100。同样下面也都要如此处理,按顺序存储,表格中代表该Cache所保存的地址的标记:
| Cache号\访问地址 | 000100 1110 | 100111 0010 | 000100 1111 | 001100 0010 | 010100 1000 | 101111 0010 | 111101 0000 | 001100 1001 |
|---|---|---|---|---|---|---|---|---|
| 是否命中 | 否 | 否 | 是 | 否 | 否 | 否 | 否 | 是 |
| 0 | 000100 | 000100 | 000100 | 000100 | 000100 | 000100 | 000100 | 000100 |
| 1 | 100111 | 100111 | 100111 | 100111 | 100111 | 100111 | 100111 | |
| 2 | 001100 | 001100 | 001100 | 001100 | 001100 | |||
| 3 | 010100 | 010100 | 010100 | 010100 | ||||
| 4 | 101111 | 101111 | 101111 | |||||
| 5 | 111101 | 111101 | ||||||
| 6 | ||||||||
| 7 |
因为Cache还有剩余,没有替换问题,所以使用三种替换算法时,Cache都是命中2次。
2)假设地址映射方式为直接映射,求Cache命中次数。
由于使用全相联映射,所以地址分为主存字块标记、Cache字块地址和字块内地址三个部分。
因为每个Cache块大小是16B,所以字块内地址还是4位。而Cache字块地址需要映射到8个Cache块,所以Cache字块地址需要三位,从而主存字块地址为10-3-4=3位。
因为直接映射法是按序存入的,所以对于同一个Cache的存储的数据的地址的Cache字块地址是固定的,所以Cache字块地址和字块内地址都不用标记,所以也只用标记主存字块标记就可以了。然后需要按照Cache字块地址来放,而不能像全映射的按0、1、2这样放。
第一个是000 100 1110,所以要将000 100 0000到000 100 1111的数据全部放入100即4号中,将4的有效位设置为1,标记000:
| Cache号\访问地址 | 000 100 1110 | 100 111 0010 | 000 100 1111 | 001 100 0010 | 010 100 1000 | 101 111 0010 | 111 101 0000 | 001 100 1001 |
|---|---|---|---|---|---|---|---|---|
| 是否命中 | 否 | 否 | 是 | 否 | 否 | 否 | 否 | 否 |
| 0 | ||||||||
| 1 | ||||||||
| 2 | ||||||||
| 3 | ||||||||
| 4 | 000 | 000 | 000 | 001 | 010 | 010 | 010 | 001 |
| 5 | 111 | 111 | ||||||
| 6 | ||||||||
| 7 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
因为使用直接映射,所以要替换一定是使用先进先出算法。
3)假设地址映射方式为二路组相联映射,在采用FIFO0、LRU、LFU替换算法时,分别求Cache命中次数。
因为使用二路组相联映射,所以使用两位来标识组,即分为4个组,每个组两个Cache块。
所以字块内地址还是4位,组地址是2位,主存字块标记位10-4-2=4位。
第一个是0001 00 1110,所以是第0组,将0001 00 0000到0001 00 1111放到第0组中,标记0001,并将0号Cache块有效位改为1,下一个是队尾,上一个是队头,FIFO算法:
| 组号 | Cache号\访问地址 | 0001 00 1110 | 1001 11 0010 | 0001 00 1111 | 0011 00 0010 | 0101 00 1000 | 1011 11 0010 | 1111 01 0000 | 0011 00 1001 |
|---|---|---|---|---|---|---|---|---|---|
| 是否命中 | 否 | 否 | 是 | 否 | 否 | 否 | 否 | 是 | |
| 0 | 0 | 0001 | 0011 | 0011 | 0011 | 0011 | |||
| 1 | 0001 | 0001 | 0001 | 0011 | 0101 | 0101 | 0101 | 0101 | |
| 1 | 2 | ||||||||
| 3 | 1111 | 1111 | |||||||
| 2 | 4 | ||||||||
| 5 | |||||||||
| 3 | 6 | 1001 | 1001 | 1001 | |||||
| 7 | 1001 | 1001 | 1001 | 1001 | 1011 | 1011 | 1011 |
由于替换只出现在0101 00 1000处,所以不同的替换策略结果相同,命中次数均为2次。
4)假设其它配置同3),采用写回法和直写法时,Cache的总容量分别为多少?
Cache容量不仅要考虑数据的大小还要考虑标记项的大小。
数据大小为8×16B=128B=1024bit。
而标记项分为有效位、标记位(地址映射)、一致性维护位(脏位,写策略)、替换算法位(替换策略)。
而按照3)的配置,地址映射的主存字块标记就是标记位内存,为4位。
而对于写策略,写回法需要脏位1位,而直写法不需要,则0位。
替换策略暂时不考虑。
所以写回法需要1024+(1+4+1)×8=1072bit,而直写法需要1024+(1+4)×8=1064bit。
虚拟存储器
- 是一个逻辑模型。
- 用户给出一个地址,叫做虚地址或逻辑地址,虚拟存储器要给出该地址对应的数据。
- 有辅助硬件将虚地址映射到主存中某个单元,主存单元地址称为实地址或物理地址。
具体内存参考操作系统,主要考页式存储,段式非常少。
页式虚拟寄存器
- 虚拟空间与主存空间都被划分成同样大小的页,主存的页称为实页,虚存的页称为虚页。
- 虚页地址分为虚页号和页内地址,实页地址分为实页号和页内地址,在虚页转换为实页时,页内地址是相同的,唯一要考虑的是虚页号如何转换为实页号。
- 做法就是将这种映射关系存在一张表中,这张表就是页表。页表存储的是实页号和装入位,用来标识虚拟地址是否装入主存地址。页表存储在主存中。
- 首先页内基表寄存器保存着页表起始地址,和虚页号进行计算就得到了页表项的地址,就可以找到页表项,把页表项中的实页地址取出并进行拼接就得到了页内地址。
- 若在Cache中存有则访问Cache,若没有再访问主存。
段式虚拟寄存器
- 段式虚拟存储器中的段是按程序的逻辑结构划分的,各个段的长度因程序而异。
- 虚拟地址分为两部分:段号和段内地址。
- 段表:每一行记录了与某个段对应的段号、装入位、段起点和段长等信息。
- 由于段的长度可变,所以段表中要给出各段的起始地址与段的长度。
段页式寄存器
- 把程序按逻辑结构分段,每段再划分为固定大小的页,主存空间也划分为大小相等的页。
- 程序对主存的调入、调出仍以页为基本传送单位。每个程序对应一个段表,每段对应一个页表。
- 虚拟地址:段号+段内页号+页内地址
快表
- 页表、段表存放在主存中,收到虚拟地址后要先访问主存,查询页表、段表,进行虚实地址转换。放在主存中的页表称为慢表(Page)。
- 提高变换速度→用高速缓冲存储器存放常用的页表项→快表(TLB)。
- TLB是Page的副本,而Cache为主存的副本。
例题 某计算机主存地址空间大小为256MB,按字节编址。虚拟地址空间大小为4GB,采用页式存储管理,页面大小为4 KB,TLB(快表)采用全相联映射,有4个页表项,内容如下表所示。
| 有效位 | 标记 | 页框号 |
|---|---|---|
| 0 | FF180H | 0002H |
| 1 | 3FFF1H | 0035H |
| 0 | 02FF3H | 0351H |
| 1 | O3FFFH | 0153H |
则对虚拟地址03FF F180H进行虚实地址变换的结果是?
对虚拟地址FF18 0180H进行虚实地址变换的结果是?
因为主存地址空间大小为256MB,若按字节编址,则有256M个地址,即28位地址。
因为虚拟地址空间大小位4GB,则同样按字节编址,则有4G个地址,即32位地址。
页面大小为4KB,则页表地址一共12位。
所以:主存地址=实页号16位+页内地址12位,虚拟地址=虚页号20位+页内地址12位。
表格的标记就是指虚页号,页框号就是指实页号。
根据虚地址组成划分所求虚拟地址为03FFF 180H和FF180 180H,根据表格对应的03FFFH指向0153H,而页内地址不变所以可以直接拼接上去得到实页地址0153180H。
同理FF180指向0002H,但是此时注意到这个快表项的有效位是0,代表TLB没有命中,所以这个地址指向不一定对,所以要去找页表,如果页表没有就再去找辅存调入主存。