基本概念
层次化结构
- 辅存中的数据调入主存才能被CPU访问
- cache用于缓解主存和CPU的速度不一致问题
分类
按层次:
- 高速储存
- 主存储器
- 辅助存储器
按介质:
- 半导体存储器:主存,cache
- 磁表面存储器:软盘,磁盘,磁带
- 光存储器:光盘
按存取方式:
- RAM:随机存取存储器,读写任何一个存储单元的时间都相同
- SAM:顺序存取存储器,读写一个存储单元所需要时间取决于所在物理位置
- DAM:直接存取存储器:有随机存取和顺序存取特性,先直接选取所在区域,然后按顺序方式存取
后面两个统称串行访问存储器
- CAM:相联存储器,可以按照内容检索到存储位置进行读写
按信息可更改性:
- 读写存储器:可读可写
- 只读存储器ROM:只能读不能写
按信息可保存性:
- 易失性存储器:断电后信息消失的存储器
- 非易失性存储器:断电后信息仍然保持
- 破坏性读出:读出后原信息被破坏
- 非破坏性读出:读出后原信息不被破坏
性能指标
- 存储容量:存储字书×字长
- 单位成本:每位价格=总成本/总容量
- 存储速度:数据传输率(主存带宽)=数据宽度/存储周期,表示每秒从主存进出信息的最大数量
- 数据宽度即为存储字长
- 存储周期指存储器进行一次完整读写所需的全部时间,即连续两次独立访问存储器操作之间所需最小时间间隔,它分为存取时间和恢复时间
基本组成
基本半导体元件
存储体内由存储元组成,存储元内含有两种半导体元件:电容和MOS管
- 读出:MOS管接入高电平导通,若电容内存储对应电荷,可以顺着MOS管输出电压,视为1,若电容内没有存储,则视为0
- 写入:MOS管接入高电平导通,输入一个高电压,电容可以保存电压,而另一边接地,可以实现存储电荷,再让MOS管断开,就可以存储1
将多个存储元的MOS管以导线连接,可以得到一整行的信息,形成存储单元,进而形成存储体
存储器芯片的基本原理
一个存储芯片由若干存储体,译码器,控制电路,MAR,MDR等
- MAR给出地址总线送来的n位地址,这n位对应\(2^n\)个存储单元
- 译码器根据MAR给出的地址,选择对应的第几根字选线,输出高电平读取到对应存储单元的信息,通过数据线将信息传送到MDR中,CPU通过数据总线取走数据
- 存储体的总容量=\(存储单元个数\times 存储字长\)
- 控制电路用于控制MAR和MDR,当MAR稳定时才会打开译码器,MDR稳定时才会通过MDR给数据总线送出数据
- 控制电路对外提供了片选线(\(\overline{CS}\)(芯片选择信号)或\(\overline{CE}\)(芯片使能信号)),低电平有效,还有读控制线和写控制线,控制读操作或写操作
- 读写控制线可能为两根,\(\overline{WE}\)表示允许写,\(\overline{OE}\)表示允许读,均为低电平有效;也有的设置为一根,低电平写,高电平读
整体看, 存储器芯片可以抽象成这种结构
译码器后面可能会接驱动器来维持电信号
片选线用于读取指定的存储芯片,给要读的芯片低电平,其余高电平
上图中的每一根线都会对应实物中的每个金属引脚
总容量=存储单元个数×存储字长
如8k×8位,前面为存储单元个数(地址线个数),后面为每个单元的存储字长(数据线个数)
共有\(2^{13}\times 8bit\)的容量
寻址
存储矩阵中,每一格表示一个字节,而每一行表示一个存储字(4B)
若总容量为1k,则存储矩阵包含256行
- 若按字节寻址,共有1k个单元,每个单元1B,需要10根地址线(\(2^{10}=1024\))
- 若按字寻址,共有256个单元,每个单元4B,需要8根地址线(\(2^{8}=256\))
SRAM和DRAM
DRAM
DRAM:使用栅极电容 电容放电时信息被破坏,属于破坏性读出,读后有重写操作
刷新
电容内的电荷只能维持2ms,需要时常刷新(给电容充电) - 一般每2ms刷新一次 - 每次刷新一行存储单元,这里的行指使用行列地址的存储单元,可以减少选通线的数量 给一个八位地址,前四位指向行地址译码器,后四位指向列地址译码器,快速找到对应存储单元 - 刷新时对应硬件读出一行的信息并重新写入,占用一个读写周期 - 刷新时刻:假设结构为128×128,存取周期0.5us,则2ms共有4000周期 - 每次读写完都刷新一次(分散刷新):系统存取周期变为1us,前半用于正常读写,后半用于刷新某行 - 2ms内集中安排时间全部刷新(集中刷新):存取周期0.5ms,有一段时间专门刷新,此时无法访问存储器,称为访问死区 - 2ms内每行刷新一次即可(异步刷新):2ms内产生128次刷新请求,每隔2ms/128一次,每15.6us有0.5us的死区 刷新由存储器独立完成,不使用CPU ## SRAM SRAM:使用双稳态触发器,由六个MOS管构成 实现双稳态:
- 输出1:A高B低
- 输出2:A低B高 读出数据时触发器状态保持稳定,属于非破坏性读出
地址线复用
将行列地址分为前后两次输出,只需要\(\frac{n}{2}\)条地址线 可以让地址线,芯片引脚更少
ROM
MROM:掩模式只读存储器 芯片生产过程中直接写入信息,之后任何人不可重写 PROM:可编程只读存储器 用户可用专门的PROM写入器写入信息,写一次后不可更改 EPROM:可擦除可编程只读存储器 允许写入信息,之后用某种方式擦除数据,可多次重写
- UVEPROM:用紫外线擦除所有数据
- EEPROM:电擦除特定字
闪存: 断电后可保存信息,可进行多次快速擦除重写,写比读慢
开机时,CPU需要先从主板上的BIOS芯片的自举装入程序读入指令引导装入操作系统 我们一般将这个芯片归属于主存,且统一编址
提升主存速度
DRAM芯片存取周期中的恢复时间较长,多核CPU均要访存 导致CPU的读写速度比主存快很多,这时我们要加速读写 ## 双端口RAM
用于优化多核CPU访问一根内存条的速度
两个端口对于同一主存操作的情况: - 同时对不同地址单元存取数据:正常 - 同时对同一地址单元读出数据:正常 - 同时对同一地址单元写入数据:写入错误 - 同时对同一地址单元一个写入一个读出:读出错误 解决方法:置忙信号为0,由判断逻辑决定暂时关闭一个端口,延长一小段时间后再访问
多体并行存储器
分为高位交叉编址和低位交叉编址 如有4个存储单元,每个单元8位,共需要\(2^5\)个地址 采用高位交叉编址可以用前两位编为体号,后三位编为体内地址,指向哪个存储体的哪一位,这样会连续访问同一个存储单元 这样每次需要等待一整个读写周期:
若采用低位交叉编址,得到的编码是相反的,这样可以依次访问不同的存储单元,每个存储单元都是读写周期结束的,因此可以在一个存储单元处于刷新时刻的时候直接读取下一个存储单元 假设每个存储体存取周期为T,存取时间为r,则连续取n个存储字的耗时可以达到\(T+(n-1)r\),明显效率更高 这种并行存取方式称为流水线方式 宏观上,一个存储周期内,m体交叉存储器可以提供的数据量为单个模块的m倍 为了使流水线不间断,应保证模块数\(m\geq \frac{T}{r}\),取等时是完美衔接的,效率最高成本最低
多体并行存储器每个模块都是独立的
采用单体多字存储器,只需要一套读写控制电路和寄存器,可以一次读写一行m个字,但不能单独区其中某个字
应用
插入内存条时,若依次按槽插入,即为高位交叉的多体存储器,若隔一个插入(或按标识),可以实现低位交叉的多体存储器,组成双通道 且推荐使用相同主频和相同容量组成,若主频不同,主频更高的会降频处理,若容量不同,低地址部分采用的是双通道,而高地址部分可能就是单通道
主存储器与CPU的连接
结构
现代计算机将MAR与MDR与CPU集成,而主存的存储芯片内只用普通寄存器暂存数据与地址,MDR通过数据总线与主存传输数据,MAR通过地址总线向主存发送地址
位扩展
通过位扩展可以增加主存存储字长
- 接入一个8k×1位的芯片,则地址有13位分别是\(A_0\sim A_{12}\),接入CPU对应地址总线部位
- WE写使能端与CPU控制总线WE端相连,传入高电平表示可写
- 由于芯片是1位的,所以每次只能通过数据总线传1bit,连接\(D_0\)
- 片选信号CS可以直接接高电平(此时高电平有效)
- 此时主存的存储字长只有1bit
连接8块1k芯片,组合成了一个8k×8位的处理器
字扩展
通过字扩展可以增加主存的存储字数
线选法
- 接入两个8k×8位的芯片,\(A_0\sim A_{12}\)同时连到两块芯片上,数据\(D_0\sim D_7\)也同时连到两块芯片上
- 若片选线全置1,则CPU发送地址时会同时传给两个芯片,两个芯片同时向数据总线传数据,导致冲突
- 因此这里需要用多出来的高位接入片选线CS,如图中\(A_{13}\)控制第一片,\(A_{14}\)控制第二片,传入高电平的CS才是这次数据传输CPU指明的芯片
- 这样有一个局限,这两位只能为10或01,有效地址段就被限制在10和01开头的地址了
- 这种方法称为线选法,若CPU有多余n个地址线,我们就只能有n个片选信号,且地址空间不连续
译码片选法
- 为了更有效利用地址,我们可以在高位地址线处接入译码器(1-2,2-4,3-8)等,这样我们可以用\(n\)个地址线表示\(2^n\)个片选信号
- 每一个高位地址对应一片存储芯片,其余的由全0到全1,且地址空间可以连续
有的译码器(如74LS138)会有多个使能端,这样CPU可以使用译码器的使能端控制片选信号的生效时间(138要求三个选通端电平情况为100)
CPU中有一个MREQ输出信号,表示主存储器请求,若CPU想访问主存,需要让该信号有效,让译码器选通
步骤如下:
- CPU送出地址信号,等待稳定
- 电信号稳定后,MREQ发出信号,让译码器某选通线有效
- 保证CS被激活时地址电信号是稳定的
## 字位同时扩展
外存储器
磁盘存储器
### 组成
存储区域
一块硬盘含若干记录面,每个记录面划分为若干条磁道,每条磁道划分为若干扇区(块),扇区是磁盘读写的最小单位
- 磁头数:记录面数,表示硬盘共有多少个磁头,磁头用于读写盘片上记录面的信息,一个记录面对应一个磁头
- 柱面数:表示硬盘每一面盘片上有多少条磁道,在一个盘组中,不同记录面的相同编号的诸磁道构成一个圆柱面
- 扇区数:每一条磁道上有多少扇区
硬盘存储器
- 磁盘驱动器:包括磁头组件和盘片组件
- 磁盘控制器:硬盘存储器和主机的接口,主流标准有IDE、SATA等
性能指标
容量:一个磁盘所能存储的字节总数,有格式化和非格式化容量之分
非格式化容量指磁记录表面可以利用的磁化单元总数
格式化容量是按照某种特定记录格式所能存储信息的总量
记录密度:盘片单位面积上记录的二进制信息量,以道、位和面密度表示
道密度:沿磁盘半径方向单位长度上的磁道数
位密度:磁道单位长度上能记录的二进制代码
注意,磁盘所有磁道记录信息量是相等的,故每个 磁道的位密度不同
面密度:位密度×道密度
平均存取时间=寻道时间(磁头移到目的磁道)+ 旋转延迟时间(磁头定位到所在扇区,默认半圈)+传输时间(传输数据所花费时间)
数据传输率:磁盘驱动器在单位时间向主机传送数据的字节数
设磁盘转速r,每条磁道容量N字节,则数据传输率为\(D_\tau = rN\)
地址
主机向磁盘控制器发送寻址信息,地址如下:
若系统中有四个驱动器,每个磁盘256磁道,16盘面,每个盘面16个扇区,则需要一个18位二进制码
工作过程
主要操作:寻址、读盘、写盘
- 每个操作对应有一个控制字,硬盘工作时,第一步是取控制字,第二部是执行控制字
- 硬盘的读写是串行的,不可以同时又读又写
磁盘阵列
RAID是将多个独立的物理磁盘组成一个独立的逻辑盘,数据在多个物理盘上交叉存储,并行访问,有更好的存储性能、可靠和安全性
RAID分级如下,在RAID1-RAID5中,无论何时有磁盘损坏,都可以随时拔出受损的磁盘再插入好的磁盘,而数据不会损坏
RAID0:无冗余和无校验的磁盘阵列。
即相邻的两个扇区存到两个磁盘,但这样扇区损坏或比特位变化时,没有冗余和校验导致无法恢复数据
RAID1:镜像磁盘阵列。
存两份数据,存储空间会浪费一半
RAID2:采用纠错的海明码的磁盘阵列。
逻辑相邻的连续几个bit物理上分散存储在各个磁盘中
另外多出几个磁盘存储海明校验位,4bit信息位+3bit海明校验位可纠正一位错
RAID3:位交叉奇偶校验的磁盘阵列。
RAID4:块交叉奇偶校验的磁盘阵列。
RAID5:无独立校验的奇偶校验磁盘阵列。
SSD
固态硬盘基于闪存技术,即EEPROM
结构
- 每个页大小为512B到4KB
- 闪存翻译层用于翻译逻辑块号,找到对应的页
- 存储介质为闪存芯片,每个芯片包含多个块,每个块包含多个页
- 以页为单位读写,若要写的页中有数据,则需要将块内其他页复制到一个新的块中,再写入新的页
- 以块为单位擦除,擦除时,会提前把其他页的数据复制到另一个块中,擦除后通过闪存翻译层重新把移动到新位置的页重新映射,若一个块被擦除次数过多,这个块可能会坏掉
- 支持随机访问,系统给定一个逻辑地址,闪存翻译层可以通过电路迅速定位到物理地址
- 磨损均衡技术:将擦除平均分布在各个块上,以提升使用寿命
- 静态磨损均衡:后台自动检测,进行数据分配和迁移,让旧闪存块承担读为主的任务,让新闪存块承担更多写任务
- 动态磨损均衡:写入数据时,优先选择累计擦除次数少的新闪存块
Cache
工作原理
CPU读写速度和内存的读写速度差距很大,因此为了缓和这个速度矛盾,我们在两者之间添加一个Cache,用于暂时存储需要用到的数据,由于它的容量小,所以能做到速度很快
实际上,现在的Cache被集成在CPU内部,用SRAM实现,速度很快
局部性原理
如一段程序:
1
2
3
4
5
6
7
8
9
10
11
12
int f(int a[N][M])
{
int sum = 0;
for (int i = 0; i<N; i++)
{
for (int j = 0; j<M; j++)
{
sum+=a[i][j];
}
}
return sum;
}这段程序访问一个二维数组并把数据加到sum中,但在内存中,这个二维数组是被一维展开的
可以发现,当我们访问其中一个数组元素时,相邻的其他数组元素也很有可能将来被访问,这就是空间局部性
空间局部性:在最近的未来要用到的信息,很可能与现在正在使用的信息在存储空间上是邻近的
又因为存在循环结构,加法指令,变量ij等在使用一次后在将来很有可能会再次被使用,这就说时间局部性
时间局部性:在最近的未来要用到的信息,很可能是现在正在用的信息
基于局部性原理,目前访问的地址周围的部分数据很可能将来还会被用到,因此我们可以把这周围的部分数据放到Cache中
若我们将上述程序改为先按列遍历,再按行遍历,则在内存中访问数据是跳跃式的,它的空间局部性更差,访问速度要慢
性能分析
设\(t_c\)为访问一次cache的时间,\(t_m\)为访问一次主存的时间
命中:CPU想要的访问的信息能在Cache中直接找到
命中率H:CPU想要访问的信息在Cache中的比率
缺失(未命中)率:1-H
平均访问时间
\(t=Ht_c+(1-H)(t_c+t_m)\):先访问cache,再访问主存
\(t=Ht_c+(1-H)t_m\):同时访问
局部的界定
如何界定CPU周围的数据?
将主存的存储空间分块,每1kb一块,主存和Cache之间以块为单位数据交换
操作系统中,通常把主存中一个块称为一个页/页面
主存与Cache的映射方式
全相联映射
也就是随便放
假设某个计算机的主存地址空间大小为256mb,按字节编址,Cache有8个Cache行,行长64B
则256mb=\(2^{28}\),主存地址共28位,每个块长64B=\(2^6\),则块内地址有6位,主存块号有\(\frac{2^{28}}{2^6}=22\)位
随意放置数据,并设置有效位表示是否有数据,设置标记为当前cache块的数据是主存哪个块号的数据
若CPU想要访问某主存地址:
- 首先对比主存地址前22位与Cache所有块中有无匹配
- 若有匹配且有效位为1,则Cache命中,访问块内地址为后六位的单元
- 若未命中或有效位为0,则正常访问主存
直接映射
固定主存块在Cache中的位置=主存块号%Cache总块数
放置位置只能固定,如将主存块号为0的放到Cache中,只能放到位置0,设置有效位与标记
其中这里标记只需要标记前19位即可,因为这里Cache只有8行,相当于对主存块号模\(2^3\),后三位直接反映了它在Cache中的位置。即若Cache总块数为\(2^n\)时,主存块号末尾n位可以直接反应它在Cache中的位置
若又想放置主存块号为8的数据,它也只能放到位置0,此时只能将原块号0的数据覆盖,并修改标记
这种有一个显然的缺点:其他地方有空闲cache块时也不能使用
若CPU想要访问主存地址:
- 首先根据主存块号后三位确定Cache行
- 若主存块号前19位于Cache标记匹配且有效位为1,可以直接访问块内地址对应的单元
- 若未命中或有效位为0,则正常访问主存
组相联映射
主存块在Cache中的分组=主存块号%分组数
放置组需要固定,假如这里使用两路组相联映射,分四组
块号为1和\(2^{22}-3\)这两块取余都为1,放在第一组,并找组内空位填充
由于只有四组,即\(2^2\),因此后两位可以直接反映它在Cache组内的位置,所以这里标记只需要标记前20位即可
若CPU想要访问主存地址:
- 根据主存块号的后2位确定分组号
- 若主存块号的前20位和分组内的某个标记匹配且有效位为1,则Cache命中,访问对应块内地址的单元
- 若未命中或有效位为0,则正常访问主存
Cache替换算法
解决Cache装满后的问题
随机算法
若Cache已满,随机选择一块替换
可以发现没有考虑局部性原理,命中率很低,效果不稳定
FIFO算法
若Cache已满,替换最先调入Cache的块
也没考虑局部性原理,最先调入Cache块的也有可能被频繁访问
会产生抖动现象:频繁的换入换出现象
LRU算法
近期最少使用算法,为每一个Cache块设置一个计数器,用于记录每个Cache块有多久没被访问,当Cache满后替换计数器最大的
步骤:
- 命中时,所命中1计数器清零,比其低的计数器+1,其余不变
- 未命中且有空闲行时,新装入行计数器置0,其余非空闲行+1
- 未命中且无空闲行时,计数值最大的行的信息块被淘汰,新装行的块的计数器置0,其余+1
Cache块总数为\(2^n\),则计数器只需要n位,且装满后所有计数器值一定不重复
若被频繁访问的主存块数量大于Cache行的数量,则有可能发生抖动
做题方法:
从左往右看,到要替换的时候从当前位置往前看,一直找到最久没访问过的,也就是最后一个出现的,就是需要替换的主存块
LFU算法
最不经常使用算法,为每个Cache块设置一个计数器,用于记录每个Cache被访问过几次,当Cache满后替换计数器最小的
步骤:
- 新调入的块计数器=0,之后每访问一次计数器+1,需要替换时,选择计数器最小的一行
- 若有多个计数器最小的行,则可以按照行号递增、或FIFO策略选择
曾经被经常访问的主存块未来不一定被用到,并没有很好遵循局部性原理,效果不如LRU
Cache写策略
若CPU修改了Cache中的数据副本,如何保存主存中数据母本的一致性?
写命中
写回法:当CPU对Cache写命中时,只修改Cache内容,而不立即写入主存,只有当此块被换出时才写回主存
修改过的Cache将脏位设置为1
这样减少了访存次数,但存在数据不一致的隐患
全写法:当CPU对Cache写命中时,必须把数据同时写入Cache和主存,一般使用写缓冲
这样访存次数增加,速度变慢
写缓冲是一个SRAM实现的FIFO队列,命中时将数据同时写入Cache和队列,当CPU干其他事情时,有专门的控制电路将队列中的数据逐一写回
若写操作很频繁,可能会因为写缓冲饱和而阻塞
写不命中
写分配法:
当CPU对Cache写不命中时,把主存中的块调入Cache,在Cache中修改,通常搭配写回法使用
非写分配法:
当CPU对Cache写不命中时,只写入主存,不掉入Cache,搭配全写法使用
多级Cache
现代计算机通常采用多级Cache,离CPU越近速度越快,容量越小,越远越慢越大
高级Cache储存的一般是低级Cache的副本
为保证数据一致性,各级Cache之间常用全写法+非写分配法,而Cache和主存之间常用写回法+写分配法