Train yourself to let go of everything you fear to lose.
存储系统基本概念
存储器的层次结构
存储器的分类
存储器的性能指标
主存储器的基本组成
基本的半导体元件及原理
存储器芯片的基本原理
寻址
DRAM和SRAM
存储元件不同导致的特性差异
DRAM的刷新
DRAM的地址线复用技术
只读存储器ROM
存储器与CPU的连接
单块存储芯片与CPU的连接
关于译码器知识的补充
双端口RAM和多模块存储器
双端口RAM
多体并行存储器
多模块存储器
Cache的基本概念和原理
Cache和主存的映射方式
Cache替换算法
Cache写策略
页式存储器
虚拟存储器
存储系统基本概念
存储器的层次结构
我们平时玩的王者荣耀,抖音是放在辅存里面的,但是要是运行需要把辅存中的数据调入到CPU,而辅存的速度又太慢,只能先将其调入到主存,然后再被CPU访问。
但是主存的速度还是不够快,我们引入了Cache,把经常使用的,例如:微信里的视频功能,存入到Cache中,一旦使用直接被CPU访问,加快速度.这就是Cache的主要作用,主要是缓解CPU和主存之间的速度矛盾
主存---辅存:实现虚拟存储系统,解决了主存容量不够的问题
Cache--辅存:解决了主存与CPU速度不匹配的问题
存储器的分类
层次
存储介质
存储器的功能:存放二进制信息
1.半导体存储器(主存,Cache):以半导体器件存储信息
2.磁表面存储器(磁盘,磁带):以磁性材料存储信息
3.光存储器:以光介质存储信息
存取方式
随机存取存储器:读写任何一个存储单元所需时间都相同,与存储单元所在的物理位置无关.
顺序存取存储器:读写一个存储单元所需要时间取决于存储单元所在的物理位置
直接存取存储器:既有随机存取特性,也有顺序存取特性。先直接选取信息所在区域,然后按顺序方式存取
相联存储器:即可以按内容访问的存储器,可以按照内容检索到存储位置进行读写,"快表就是一种相联存储器
顺序,直接存取存储器,称为串行访问存储器,读写某个存储单元所需时间与存储单元的物理位置有关.
信息的可更改性:
读写存储器:即可读,也可写(如:磁盘,内存,Cache)
只读存储器:只能读,不能写
信息的可保存性
断电后,存储信息消失的存储器--易失性存储器(主存,Cache)
断电后,存储信息依然保持的存储器--非易失性存储器(磁盘,光盘)
信息读出后,原存储信息被破坏--破坏性读出(如DRAM芯片)
信息读出后,原存储信息不被破坏--非破坏性读出(如SRAM芯片)
存储器的性能指标
1.存储容量;存储字数*字长(前面讲过,此处不做过多赘述)
2.单位成本:每位价格=总成本/总容量
3.存储速度:数据传输率=数据的宽度/存储周期(数据的宽度:存储字长,存储周期=存取时间+恢复时间)
主存储器的基本组成
基本的半导体元件及原理
读出:WMOS管:可理解为一种电控开关,输入电压达到某个阈值时,MOS管就可以接通,MOS就是一个典型的半导体元件;电容的下面是接地的,当电容上下存在明显电压差,就会产生电浮,计算机中用二进制1表示,MOS管中电压达到某个阈值时,就会接通,将1传入到另一边
写入:再导线的右端加一个电压,用二进制1表示,然后MOS管接通,1进来,电容就会存在电压差,接下来断开MOS管,这样电容里的电荷就跑不出去了.
然后我们将多个存储元连在一起,我们就一次性可以读出或写入多个二进制数据;我们称为存储单元.将多个存储单元称为存储体;
存储器芯片的基本原理
n位地址->2^n个存储单元;
总容量=存储单元个数*存储字长=2^3*8bit=2^3*1Byte=8B
译码器:根据地址寄存器给出的n位地址,转换成某一条选通线的高电平信号;
如图MAR:3位地址->2^3个存储单元;
接下来我们继续完善存储器的构成,我们需要增加控制电路
由于我们使用电信号传入,难免有不稳定的时候,当MAR里面的电信号稳定之前,控制电路是不会让其进入的,只有稳定时候才会打开译码器的开关,让译码器来翻译这个地址;当数据输出的时候,只有当MDR稳定的时候才会执行.
CS:芯片选择信号(上面有个线,不会打,可以看图中)
CE:芯片使能信号
这样就完成了一个完整构造的存储芯片;
n位地址->2^n个存储单元;
总容量=存储单元个数*存储字长=2^3*8bit=2^3*1Byte=8B
8*8的存储芯片
常见的描述:8K*8位,即2^13*8bit;8K*1位,即2^13*1bit;64K*16位,即2^16*16bit
寻址
现在计算机当中通常是按字节编制寻址的.每个小矩阵是8bit也就是1B;
这个存储矩阵总容量为1KB;
按字节寻址,1K个单元,每个单元1B;1K个单元,说明地址线有10根,2^10=1K
按字寻址:256个单元,每个单元4B;
按半字寻址:512个单元,每个单元2B;
按双字寻址:128个单元,每个单元8B;
DRAM和SRAM
存储元件不同导致的特性差异
Dynamic Random Access Memory 即动态RAM
Static Random Access Memory 即静态RAM
DRAM用于主存,SRAM用于Cache
DRAM芯片:使用栅极电容存储信息
SRAM芯片:使用双稳定触发器存储信息
核心区别:存储元不一样
栅极电容V.S.双稳态触发器
栅极电容
读出1:MOS管接通,电容放电,数据线上产生电流
读出电容后,也就是电容放电信息被破坏,是破坏性读出.读出后应有重写操作,也称"再生"
因此读写速度慢
读出0:MOS管接通后,数据线上无电流;
因为只有一个存储元制造成本更低一点,集成度高,功耗低
双稳态触发器
1:A高B低; 0:A低B高
读出数据,触发器状态保持稳定,是非破坏性读出,无需重写,因此读写速度快;
每个存储元制造成本更高,集成度低,功耗大
对比DRAM和SRAM
类型特点 SRAM(静态RAM) DRAM(动态RAM) 存储信息 触发器 电容 破坏性读出 非 是 读出后需要重写? 不用 需要 运行速度 快 慢 集成度 低 高 发热量 大 小 存储成本 高 低 易失/非易失性存储器? 易失 易失(断电后信息消失) 需要刷新? 不需要 需要(分散,集中,异步)
"刷新"由存储器独立完成,不需要CPU控制
送行列地址 同时送 分两次送(地址线复用技术)
导致地址线,地址引脚减半
用作Cache 用作主存 栅极电容:电容内的电荷只能维持2ms.即便不断电,2ms后信息也会消失;也就是2ms之内必须刷新一次(给电容充电)
双稳态触发器:只要不断电,触发器的状态就不会发生改变
DRAM的刷新
DRAM的刷新
1.多久需要刷新一次?刷新周期:一般为2ms
2.每次刷新多少存储单元?以行为单位,每次刷新一行存储单元
为什么要用行列地址?
减少选通线的数量;像2^8=256根选通线,可以排成2^4+2^4=32根选通线;
3.如何刷新:有硬件支持,读出一行的信息后重新写入,占用1个读/写周期
4.在什么时刻刷新?
DRAM的地址线复用技术
为了让地址线变的更少和更简单,我们采用DRAM的地址线复用技术,
行,列地址分两次送,可使地址线更少,芯片引脚更少
只读存储器ROM
RAM芯片——易失性,断电之后数据消失
ROM芯片------非易失性,断电之后数据不会丢失
MROM--掩模式只读存储器
厂家按照客户需求,在芯片生产过程中直接写入信息,之后任何人不可重写(只能读出),可靠性高,灵活性差,生产周期长,只适合批量定制.
PROM--可编程只读存储器
用户可用专门的PROM写入器写入信息,写一次之后就不可更改
EPROM--可擦除可编程只读存储器
允许用户写入信息,之后用某种方法擦除数据,可进行多次重写。
UVEPROM--用紫外线照射8-20分钟,擦除所有信息
EEPROM--可用"电擦除"的方式,擦除特定的字
Flash Memory--闪速存储器(U盘,SD卡就是闪存)
在EEPROM基础上发展而来,断电后能保存信息,且可进行多次快速擦除重写
SSD--固态硬盘
由控制单元+存储单元(Flash芯片)构成,与闪速存储器的核心区别在于控制单元不一样,但存储介质都类似,可进行多次快速擦除重写.SSD速度快,功耗低,价格高。目前个人电脑上常用SSD取代传统的机械硬盘
计算机内的重要ROM
CPU的任务:就是到主存中取指令并执行指令;
RAM:断电后,RAM内的数据全部丢失;
例如计算机关机后,主存里数据会全部丢失,CPU需要读取开机等指令,但是RAM数据全部丢失,所以需要从主板上的BIOS芯片(ROM)读取开机所需要的指令(BIOS芯片(ROM)存储了"自举装入程序"负责引导装入操作系统(开机)).
注:我们常说"内存条"就是"主存"但事实上,主板上的ROM芯片也是"主存"的一部分
逻辑上,主存由RAM+ROM组成,且二者统一编址.
存储器与CPU的连接
单块存储芯片与CPU的连接
这个是8*8位的存储芯片;如果我们想要扩展主存字数怎么办?假如数据总线宽度>存储芯片字长怎么办?
虽然现在的MAR和MDR都画在存储器中,但现在的计算机MAR,MDR通常集成在CPU内部。存储芯片内只需一个普通的寄存器(暂存输入,输出数据)
现在的计算机
存储芯片的输入输出信号
片选信号:低电平有效(CS或CE)上面上划线;高电平有效(CS或CE)
读/写控制线:低电平有效(WE或WR)上面有上划线,高电平有效(WE或WR)
增加主存的存储字长---位扩展
8K就是2^13次方,所以我们要用13根地址线,A0-A12,WE代表write Enable不带上面的线,说明
当这个信号是高电平的时候,此时往里面写数据;CS上面没有画横线,直接给他接上高电平;
此时我们主存只有一块存储芯片,每一次只能读或写一位数据,所以此时主存的存储字长只有1bite,数据总线并没有被充分的利用;为了解决这个问题,我们给主存再加上相同型号的一个存储芯片;
接下来我们使用同样的方法,最终就可以得到:
8片8K*1位的存储芯片->1个8K*8位的存储器,容量8KB;
增加主存的存储字数--字扩展
按照上面的说法,连接后,发现A13,A14,A15这三根线会浪费掉,再仔细观察上面这个图给这两个芯片都是高电平1,他们都在工作,会导致数据总线D0-D7的冲突,那么如何解决这个问题囊?关键在于片选线的应用,当A13为1,A14为0时第一个芯片工作,第二个芯片不工作,反之则第二个工作,但是A13,A14不能同时为1,0;
这种方法称为线选法:A14,A133只能为01或10n条线->n个选片信号;
接下来对线选法进行优化,给A13接一个非门处理;
那么第一个芯片最低地址:100 0000 0000 0000;最高地址:11 1111 1111 1111;
第二芯片最低地址:00 0000 0000 0000;最高地址:01 1111 1111 1111
上面的我们称为1-2译码器;其实之前我们接触过更复杂的译码器,译码器片选法:n条线->2^n个片选信号;
我们接下来再看一个2-4译码器
按照同样的方法,把A15也接上,就是3-8译码器
既然有字扩展,位扩展,那么有没有两种结合
主存容量扩展--字位同时扩展
关于译码器知识的补充
这里我们给出3-8译码器,有三个输入端,八个输出端,译码器Y0-Y7只会有一条线是高电平,那么这个译码器就可以和高电平有效存储芯片配合使用;
那么我们来看译码器的另一种画法,译码器的右边画一个小圆,低电平有效;
那我们来看一种更复杂的情况
注:CPU可使用译码器的使能端控制片选信号的生效时间
双端口RAM和多模块存储器
存取周期
注:DRAM芯片的恢复时间比较长,有可能是存取时间的几倍(SRAM的恢复时间较短)
存取周期:可以连续读/写的最短时间间隔
如:存取时间为r,存取周期为T,T=4r;
多核CPU都要访存,怎么办?
CPU的读写速度比主存快很多主存恢复时间太长怎么办?
双端口RAM
作用:优化多核CPU访问同一根内存条的速度
需要有两组完全独立的数据线,地址线,控制线,CPU,RAM中也要有更复杂的控制电路
两个端口对同一主存操作有以下4种情况:
1.两个端口同时对不同的地址单元存取数据
2.两个端口同时对同一地址单元读出数据
3.两个端口同时对同一地址单元写入数据:写入错误
4.两个端口同时对同一地址单元,一个写入数据,另一个读出数据:读出错误
为了解决3和4这种情况,RAM向CPU发出"忙"的信号,置"忙"的信号为0,由判断逻辑决定暂时关闭一个端口(即被延时),未被关闭的端口正常访问,被关闭的端口延长一个很短的时间段后再访问。
多体并行存储器
即便对于单核CPU,CPU的读取速度也比内存快得多,而内存读取后需要恢复时间后才能继续读取。这个问题的解决就可以使用多体并行存储器
高位交叉编址的多体存储器
可理解为"四根内存条":4*8=32=2^5;所以我们可以用5个bite作为主存的地址,高位的两个数表示体号,地址后面三位数表示体内地址.
低位的两个数表示体号,前三个数表示体内地址
每个存储体存取周期为T,存取时间为r,假设T=4r;
连续访问00000,00001,00010,00011,00100情况下:
低位交叉编址
下面的低位交叉编址的多体存储器耗时T+4r;而高位交叉编址的多体存储器耗时为4T;性能几乎提升了4倍
应该取几个"体"囊?
采用"流水线"的方式并行存取(宏观上并行,微观上串行)
宏观上,一个存储周期内,m体交叉存储器可以提供的数据量为单个模块的m倍。
存取周期为T,存取时间为r,为了使流水线不间断,应保证模块数m>=T/r.
m<T/r时
m>T/r时
m=T/r时
多模块存储器
多体并行存储器
每个模块都有相同的容量和存取速度。各模块都有独立的读写控制电路,地址寄存器和数据寄存器。他们既能并行工作,又能交叉工作.
单体多字存储器
每个存储单元存储m个字总线宽度也为m个字,一次并行读出m个字。
每次只能同时取m个字,不能单独取其中某个字,指令和数据在主存内必须时连续存放的
Cache的基本概念和原理
双端口RAM,多模块存储器提高存储器的工作速度,优化后速度与CPU差距依然很大
如何解决:我们可以设计更高速的存储单元,这就意味着我们存储价格就会更高;基于程序的局部性原理,我们增加一个"Cache-主存"层次
Cache的工作原理
注:实际上,Cache被集成在CPU内部Cache用SRAM实现,速度快,成本高;
空间局部性:在最近的未来要用到的信息(指令和数据),很可能与现在正在使用的信息在存储空间上是邻近的
时间局部性:在最近的未来要用到的信息,很可能是现在正在使用的信息
基于局部性原理,不难想到,可以把CPU目前访问的地址"周围"的部分数据1放在Cache中
设tc为访问一次Cache所需时间,tm为访问一次主存所需时间‘
命中率H:CPU欲访问的信息已在Cache中的比率
缺失(未命中)率M=1-H
Cache-主存 系统的平均访问时间t为
t=Htc+(1-H)(tc+tm)
先访问Cache,若Cache未命中再访问主存
或 t=Htc+(1-H)tm:同时访问Cache和主存,若Cache命中则立即停止访问主存
例题:
有待解决的问题
基于局部性原理,不难想到,可以把CPU目前访问的地址'周围:的部分数据放到Cache中,如何界定"周围"?
将主存的存储空间"分块',如:每1KB为一块,主存与Cache之间以"快"为单位进行数据交换
- 如何区分Cache与主存的数据块对应关系?--Cache和主存的映射方式
- Cache很小,主存很大.如果Cache满了怎么办?--替换算法
- CPU修改了Cache中的数据副本,如何确保主存中数据母本的一致性?--Cache写策略
下面的章节我将跟大家一起学习:
Cache和主存的映射方式
全相联映射--主存块可以放在Cache的任意位置
不了解有效位和标记是啥的可以先往下看,最后面有讲解
采用这种方式,CPU如何访问一个主存地址囊?
假设CPU访问主存地址,1..1101001110:
1.主存地址的前22位对比Cache中所有块的标记:
2.若标记匹配且有效位=1,则Cache命中,访问块内地址为001110的单元.
3.若未命中或有效位=0,则正常访问主存
直接映射--每个主存快只能放到一个特定的位置:Cache块号=主存块号%Cache总块数
直接映射,主存在Cache中的位置=主存块号%Cache总块数
例如主存中块号为0的0%8=0存储在Cache0的位置,主存中块号8,8%8=0,也是存储在0的位置,
缺点:其他地方有空闲Cache块,但是8号主存块不能使用
能否优化标记?
这里Cache有8行,主存块号%2^3,相当于留下最后三位二进制数;那么就意味着计算机只要读取22位中的后三位;
若Cache总块数=2^n则主存块号末尾n位直接反映它在Cache中的位置,将主存块号的其余位作为标记即可,Cache中只需要标记前19位,末尾三位不需要标记;
如何访存?
CPU访问主存地址:0...01000 001110:
1.根据主存号的后3位确定Cache行。
2.若主存块号的前19位与Cache标记匹配且有效位=1,则Cache命中,访问块内地址位001110的单元
组相联映射--Cache块分为若干组,每个主存块可放到特定分组中的任意一个位置组号=主存块号%分组数
主存块号%2^2,相当于我们只保留了主存块号的末尾两位,因此只需要看末尾两位就可以确定在被分配到哪,因此Cache中的标记只需要采用前20位即可。
CPU访问主存地址:1...1101001110:
1.根据主存块号的后两位确定所属分组号
2.若主存块号的前20位与分组内的某个标记匹配且有效位=1,则Cache名中,访问块内地址为001110的单元.
那么如何区分Cache中存放的是哪个主存块?
给每个Cache块增加一个"标记",记录对应的主存块号?光有"标记"就行了嘛?
假设9,8,5代表的是主存中数字在Cache中存放的位置;0代表没有空闲的位置,那主存中也存在0这个位置,不就表达有歧义了嘛?所以还要增加"有效位:1代表存储,0代表空闲
Cache替换算法
全相联映射
Cache完全满了才需要替换需要在全局选择替换哪一块
直接映射
如果对应位置非空,则毫无选择地直接替换
组相联映射
分组内满了才需要替换需要在分组内选择替换哪一块
随机算法--若Cache已满,则随机选择一块替换;
设总共有4个Cache快,初始整个Cache为空.采用全相联映射,依次访问主存块{1,2,3,4,1,2,5,1,2,3,4,5}
随机算法--实现简单,但完全没考虑局部性原理,命中率低,实际效果很不稳定
先进先出算法--若Cache已满,则替换最先被调入Cache的块
设总共有4个Cache快,初始整个Cache为空,采用全相联映射,依次访问主存块
{1,2,3,4,1,2,5,1,2,3,4,5}
先进先出算法--实现简单,最开始按#0#1#2#3放入Cache,之后轮流替换#0#1#2#3FIFO依然没考虑局部性原理,最先被调入Cache的块也有可能是被频繁访问的
近期最少使用算法--为每一个Cache块设置一个"计数器",用于记录每个Cache块已经有多久没被访问了,当Cache满后替换"计数器"最大的
设总共有4个Cache快,初始整个Cache为空,采用全相联映射,依次访问主存块
{1,2,3,4,1,2,5,1,2,3,4,5}
1.命中时,所命中的行的计数器清零,比其低的计数器加1,其余不变
2.未命中且还有空闲行时,新装入的行的计数器置为0,其余非空闲行全加1
3.未命中且无空闲行时,计数值最大的行的信息块被淘汰,新装行的块的计数器置为0,其余全加1
上面演示了部分步骤,剩下大家可以试着算一下
LRU算法--基于"局部性原理,近期被访问过得主存块,在不久的将来也很可能被再次访问,因此淘汰最久没被访问过得快是合理的.LRU算法的实际运行效果优秀,Cache命中率高.
最不经常使用算法--为每一个Cache块设置一个"计数器:"用于记录每个Cache块被访问过几次,当Cache满后替换"计数器"最小的
设总共有4个Cache快,初始整个Cache为空,采用全相联映射,依次访问主存块{1,2,3,4,1,2,5,1,2,3,4,5}
新调入的块计数器=0,之后每被访问一次计数器+1,需要替换时,选择计数器最小的一行
若有多个计数器最小的行,可按行号递增,或FIFO策略进行选择
LFU算法1--曾经被经常访问的主存块在未来不一定会用到(如;微信视频聊天相关的块)
并没有很好地遵循局部性原理,因此实际运行效果不如LRU
Cache写策略
写回法--当CPU对Cache写命中时,只修改Cache的内容,而不立即写入主存,只有当此块被换出时才写回主存,减少了访存次数,但存在数据不一致的隐患
增加一个脏位表示是否被修改过.
全写法--当CPU对Cache写命中时,必须把数据同时写入Cache和主存,一般使用写缓冲
访存次数增加,速度变慢,但更能保证数据一致性
使用写缓冲,CPU写的速度很快,若写操作不频繁,则效果很好,若写操作很频繁,可能会因为写缓冲饱和而发生阻塞
写分配法--当CPU对Cache写不命中时,把主存中的块调入Cache,在Cache中修改。通常搭配写回法使用
非写分配法--当CPU对Cache写不命中时只写入主存,不调入Cache,搭配全写法使用
现在计算机通常采用多级Cache,离CPU越近速度越快,容量越小;离CPU越远的速度越慢,容量越大;各级Cache间常采用"全写法+非全写分配法",Cache和主存间通常采用"写回法+写分配法"
页式存储器
假如一个微信有1GB大小,全部放入内存,并且需要连续存放,在很多时候会导致主存的利用率不高;为了让主存的利用率更高,就拿某个程序(4KB)来说,我们会把4KB的程序分为4个"页",每个页面的大小相同
页面存储系统:一个程序在(进程)在逻辑上被分为若干个大小相等的"页面","页面"大小与"块"的大小相同,每个页面可以离散地放入不同的主存块中.
虚地址VS实地址
逻辑地址(虚地址):程序员视角看到的地址
物理地址(实地址):实际在主存中的地址
逻辑页号->主存块号
地址变换过程
地址变换过程(增加TLB)
虚拟存储器
思考:打游戏时候的"Loading"界面背后是在干嘛?
--将游戏地图相关数据调入内存
页式虚拟存储器
有效位:当有效位为0说明程序还在辅存中,还没有被调入到主存;反之亦然
外存块号:可以找到每个页面在外存中存放的位置
访问位:页面替换算法;解决主存和辅存之间的替换
脏位:这个页面是否被修改过
段式虚拟存储器
按照功能模块拆分如:#0段是自己写的代码,#1段是库函数代码,#2是变量