知识总览:
主存——辅存:解决了主存容量不够的问题。
Cache——主存:解决了主存与CPU之间速度不匹配的问题。
(1)按层次进行分类
(2)按传输介质进行分类
(3)按存取方式进行分类
顺序存取存储器和直接存取存储器都归为串行访问存储器(读取时间和数据位置有关)。
(4)按信息的可更改性进行分类
(5)按信息的可保存性进行分类
MAR反映存储字数,MDR反映了存储字长。
单位成本:每位价格 = 总成本/总容量(某条内存条价格为259,容量为8GB,则单位成本 = 259 / (8*8) )。
存储速度:数据传输率 = 数据的宽度/存储周期
存取时间:存取时间 + 恢复时间 = 存取周期,计算机进行一次读写操作后需要一段恢复时间才能进行下一次的读写操作。
主存带宽:就是上面的数据传输率,表示每秒从主存进出信息的最大数量,单位为字/秒,字节/秒,位/秒。
知识总览:
(1)存储体
存储体由多个存储单元构成,每个存储单元由多个存储元构成。
存储元:能够存储一位比特。
电容:存储电荷,有电压差表示比特1,无电压差表示比特0。
MOS管:一个电控开关,当输入电压到达某个阀值时,MOS管就可以接通,电容中的电荷就可以跑出来。(相当于一个电路的开关)
将多个存储元组合起来就构成了存储单元,同个存储单元的存储元共用一根MOS管。
将多个存储单元组合起来就构成了存储体。
(2)地址寄存器和数据寄存器
通过 译码器来选择哪一个存储单元进行读写。
并通过数据总线传送数据。
(3)控制电路:
控制电路控制着 MAR,译码器和MDR的数据处理。
同时存储芯片还需要对外提供 片选线,读选择线,写选择线。
片选线:芯片的总开关,低电平有效时表示选择了这块芯片进行工作了。
读选择线,写选择线:分别用来控制是读还是写。有时候会将这两根线合为一条,用低电平表示写,用高电平表示读。
(4)从总体上来看:
这里也表明了片选线的作用,上述的内存条是由多个存储芯片构成,假设每个存储芯片为1GB,则这条内存条条就为8GB,在进行读写数据时,需要通过片选线来选取不同的存储芯片进行存储。
寻址:
假设该存储体中的存储字长为4B,总容量为1KB,则存储单元为256个。
寻址方式可分为四种:
知识总览:
RAM主要分为两种:
SRAM:静态RAM,用于Cache
DRAM:动态RAM,用于主存
DRAM芯片:使用栅极电容来存储信息
SRAM芯片:使用双稳态触发器来存储信息
(1)栅极电容:
1:电容内存储了电荷
0:电容内未存储电荷
破坏性读出:电容放电后相当于电荷跑了出来,导致信息破坏,所以读出后需要有重写操作,也称为再生。这也是DRAM读写速度比SRAM慢的原因。
电容内的电荷只能维持2ms,即便不断电,2ms后信息也会消失。所以在2ms之内必须刷新一次,给电容充电。
每个存储元制造成本更低,集成度高,功耗低。
(2)双稳态触发器:
1:A处为高电平并且B处为低电平
0:A处为低电平并且B处为高电平
非破坏性读出:读出数据后,触发器状态保持稳定,无需重写。读写速度更快。
只要不断电,触发器的状态就不会改变。
每个存储元制造成本更高,集成度低,功耗高。
(1)多久刷新一次:一般为2ms
(2)每次刷新多少个存储单元:以行为单位,每次刷新一行存储单元。
存储器中存储单元的排列是由 行地址译码器 和 列地址译码器 共同选择。
如果只使用一个译码器来进行地址选择的话,那么8位地址需要使用 28=256 条选通线,而使用行地址译码器和列地址译码器共同工作的话,只需要 24+24=32 条选通线,也就是行译码器16条选通线,列译码器16条选通线。
(3)如何刷新:有硬件支持,读出一行的信息后重新写入,占1个读/写周期。
(4)什么时候进行刷新?
假设DRAM内部结构排列成 128x128 的形式,读/写周期为 0.5us ,则 2ms 内共有 2ms/0.5us=4000个 读/写周期。
于是刷新的时间选择可以有以下三种思路:
由于DRAM存储容量较大,存储单元数目多,所需地址线也就比较多,为了简化硬件,采用地址线复用技术,行地址和列地址分成两次进行传送到译码器中。
总结:
假设有一块 8Kx1位 的存储芯片,说明此芯片需要 13根地址线,CPU有8个数据线和16根地址线,从上面可以看出,CPU每次只能传送1位数据到该芯片中,这会导致CPU的数据线没有得到充分利用,并且会有需要扩展主存容量的情况。
于是,就需要通过多个存储芯片的合理连接来扩展主存容量。
可以有两种方式:
(1)位扩展:
如上图,将两片存储芯片进行连接,CPU地址总线A0~A12会一起发送给这两块芯片,而第二块芯片的数据线D0会和CPU的数据线D1进行相连。这样就扩展成了8Kx2位的存储器了。
同样,CPU中一共有D0~D7个数据线,因此可以总共连接8个图中所示的存储芯片,扩展成 8Kx8位的 存储器。
(2)字扩展:
字扩展可分为两种:
如图中是一块 8Kx8位 的存储芯片,由于该芯片的存储单元字长等于CPU的数据总线数,因此不需要进行位扩展。而地址线并没有被完全利用到,因此可以考虑进行字扩展。
用专门的地址线作为片选线,进行选择某一块存储芯片。
如图中:A13为第一块存储芯片的片选线,A14为第二块存储芯片的片选线,当A13 A14分别为01或10时,代表选中了第一块存储芯片或第二块存储芯片,这样就能扩展存储单元的个数。
此时第一块存储芯片的最低地址为:01 0000 0000 0000,最高地址为 01 1111 1111 1111
第二块存储芯片的最低地址为:100 0000 0000 0000,最高地址为 101 1111 1111 1111
但是线选法有一个缺陷:A13和A14不能取00或者11,因为取00时会导致两快存储芯片都没有被选中,11会导致两块存储芯片都被选中。并且需要n个片选信号。
可以进行优化:
只通过A13和一个非门来进行选择。
此时第一块芯片的最低地址为10 0000 0000 0000,最高地址为11 1111 1111 1111
第二块芯片的最低地址为00 0000 0000 0000,最高地址为01 1111 1111 1111
总结:
(3)位扩展和字扩展 同时扩展
知识总览:
前面讲到,DRAM由于电容的特性,每次进行读操作后需要进行重写,也就是需要一定的恢复时间,导致DRAM的存取周期比较长,并且这个恢复时间往往还比存取时间长。
这样会引出以下两种问题:
对于现如今的计算机,很多都是具有多核CPU,那么不同的CPU访问主存时,后续的CPU是否需要等待这么长的恢复时间呢?
CPU的读写速度比主存快很多,主存恢复时间太长怎么办?
这两个问题分别对应接下来的内容:双端口RAM 和 多模块存储器。
**需要两组完全独立的数据线,地址线, 控制线。**CPU和RAM中也要有更复杂的控制电路。
两个端口对同一主存操作有以下4种情况:
当发生冲突时,会发出忙信号,有判断逻辑决定暂时关闭一个端口(即被延时),未被关闭的端口正常访问,被关闭的端口延长一个很短的时间断后再访问。
(1)多体并行存储器:
图中每个方格表示一个存储单元。
高位交叉编址:每个存储体遍历完了之后再遍历下一个存储体。仅仅相当于扩容。
低位交叉编址:以横向方式遍历存储体,也就是每次都是遍历不同存储体的不同存储单元。这样,在读取其他存储体的存储单元时,之前读取过的存储单元可以利用这段时间进行恢复,达到一种并行的效果。
在模块数的选取上需要保证模块数 m >= T/r,这样才能保证存储体有足够的时间进行恢复。
(2)单体多字存储器:
和多体并行存储器相反,单体多字存储器将多个存储体进行了合并,每次只能同时取m个字,不能单独取其中某个字,当某次读写的数据在不同行时,需要读入多余的信息。
双通道内存:
原理:低位交叉编址的多体并行存储器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CwU4VSKa-1627306760688)(https://gitee.com/oymn/img_upload/raw/master/img/20210625115529.png)]
知识总览:
虽然 双端口RAM和多模块存储器已经提高了存储器的工作速度,但是和CPU的差距依然很大,于是就产生了缓存Cache,其价格更高,容量更低。
(1)工作原理:
**空间局部性:**在最近的未来要用到的信息,很有可能与闲杂i正在使用的信息在存储空间上是临近的。(信息的存储一般都是顺序的)
**时间局部性:**在最近的未来要用到的信息,很可能是现在正在使用的信息。(程序中很可能存在大量的循环结构,需要重复访问)
故Cache能够工作的理论依据:从上面两个局部性可以分析到,我们可以把CPU目前访问的地址周围的部分数据放到Cache中。
(2)性能分析:
设 **tc**为访问一次Cache所需时间,**tm**为访问一次主存所需时间。
CPU能够直接在Cache中找到所需信息,称为命中。
**命中率 H:**CPU欲访问的信息已在Cache中的比率。
**缺失率(未命中率)M:**M = 1 - H
**CPU平均访问时间 t 为:**t = H tc + ( 1-H ) ( tc+tm )
例题:
(3)如何界定“周围”:
将主存的存储空间进行分块,主存与Cache之间以块为单位进行数据交换。
这样主存地址就可分为两部分:块号 和 块内地址。
在操作系统中,通常将主存中的 “一个块” 也称为 “一个页/页面/页框” 。
Cache中的 “块” 也称为 “行” 。
Cache和主存之间仍然存在一些问题:
以上问题请继续看下面内容。
总结:
任何一个主存块可以放在Cache的任意位置。
给每一个Cache块增加一个标记位,记录对应的主存块号。
还需要一个有效位,有效位为1时表示标记位有效,有效位为0时表示标记位无效。
以上图为例,主存中任意一块数据都可以存放在Cache任意一块中,只要标记好和主存数据的对应就行。例如主存中第9号块数据可以存放在Cache中0号到1号任意一个位置。
优点:Cache存储空间利用充分,命中率高。
缺点:查找速度较慢。
故CPU访问主存数据的流程如下:
每个主存块只能放到特定的一个位置:Cache块号 = 主存块号 % Cache总块数。
以上图为例,8块为一个循环。主存中0号和8号块数据可以存放在Cache第0号块中,主存中1号和9号块数据可以存放在Cache第1号块中,以此类推。
**若Cache总块数 = 2n,则主存块号末尾 n 位直接反映它在Cache中的位置。**这是因为Cache块号 = 主存块号 % Cache总块数 = 主存块号末尾 n 位。
于是对于Cache所保存的标记号可以不用保存主存中对应的整个地址,可以只保留主存块号m前面(m-n)个二进制数。
故CPU访问主存数据流程如下:
优点:只需要对比一个标记,速度最快。
缺点:Cache存储空间不充分,命中率低。
将Cache块分为若干组,每个主存块可放到特定分组中的任意一个位置。
每个主存块对应的组号 = 主存块号 % 分组数
以上图为例,8块为一个循环组。0号和8号同属于第0组,0号和8号数据可以存放在第0组的任意一个位置,只要做好标记就行。
和直接映射一样,若Cache总分组数 = 2n,则主存块号末尾 n 位直接反映它在Cache中是第几组。
故CPU访问主存数据流程如下:
假设以下采用2路组相联映射,也就是 2块为一组,分四组。
优点:上面两种方法的折中,综合效果较好。
总结:
知识总览:
直接映射方式 由于主存数据块对应的Cache块是固定的,所以没得选择,不需要使用替换算法。
而全相联映射和组相联映射方式 由于可以选择不同位置进行替换,所以需要用到替换算法进行替换。
下面例子以全相联映射方式进行举例。
若Cache已满,则随机选择一块替换。
举例:
图中红色表示该位置被重新替换成新的块。
图中绿色表示命中该缓存块。
举例:
从上图可以看到,红色出现很多次,这表明缓存块被频繁地替换,刚被替换的块很快又被重新调入,这称为抖动现象。
举例:
一开始,缓存块中并没有存储数据,计数器均为0。
访问主存块1
访问主存块2
访问主存块3
访问主存块4
访问主存块1,命中缓存,Cache0计数器重新清零
访问主存块2,命中,Cache1计数器重新清零
访问主存块5,此时未命中,将会采取下先进先出算法,替换计数值最大的块,图中替换Cache2
接下来的流程和上面类似,只需要根据计数规则自行模拟即可,这里不再赘述。
为每一个Cache块设置一个”计数器“,用于记录每个Cache块被访问过几次。当Cache满后替换“计数值”最小的。
新调入的块计数器=0,之后每被访问过一次计数器+1。
举例:
访问了主存块1号,2号,3号,4号后,Cache的状态:
访问主存块1号和2号时均命中,Cache0和Cache1计数器加一。当访问主存块5号时,未命中,于是会替换计数值最小的Cache2(这里Cache2和Cache3均为0,会按行号递增或者FIFO策略来进行选择哪一个被替换)
接下来的流程和上面类似,根据 最不经常使用算法 自行模拟即可,这里不再赘述。
总结:
知识总览:
为何不讨论读命中,读不命中的情况?
因为读不会导致主存和Cache的数据不一致。
(1)写回法:
当CPU对Cache写命中时,只修改Cache的内容,而不立即写入主存,只有当此Cache块被替换时才写回主存。
需要增加一位脏位,用于标记该Cache块是否被修改了。
减少了访存的次数,但存在数据不一致的隐患。
(2)全写法:
(1)写分配法:
(2)非写分配法:
现代计算机常采用多级Cache:
离CPU越近的速度越快,容量越小
离CPU越远的速度越慢,容量越大。
各级Cache之间常采用 全写法 + 非写分配法。
Cache 和 主存 之间常采用 写回法 + 写分配法。
总结:
有时候一个程序比较大,无法将它顺序地存放在主存中的各个块中,因此操作系统会将它分成若干个页面,页面的大小和块的大小相同,每个页面可以离散地放入不同的主存块中。
逻辑地址(虚地址):程序员看到的地址。
物理地址(实地址):实际在主存中的地址。
对于一条机器指令:000001 001000000011(操作码+地址码),其地址码所使用的就是 逻辑地址。
页表:记录了每个逻辑页面存放在哪个主存块中,页表的数据放在主存中。
地址变换过程:举例
假设此时执行一条取指令操作,该指令为 000001 001000000011,所以要取的数据的逻辑地址为 001000000011
- 首先将逻辑地址拆分成 逻辑页号 和 页内地址。
- 在主存中找到 页表,然后通过 逻辑页号 进行查表,查出对应的 主存块号。
- 将 主存块号 拼接 页内地址 得到最终的 物理地址。
- 到Cache中查找该物理地址,找不到的话再到主存找。
但是由于页表是存放在主存中的,而根据时间局部性原理,刚使用的数据很可能在接下来还会使用,这样在下一次使用该数据时又得访问一次主存进行查表,为了更快地查表,会将近期访问的页表项放入更高速的存储器中,称为 快表。
**快表:**快表是一种相联存储器,可以按照内容进行寻访,并且其采用的是SRAM,访问速度更快。
快表 和 Cache 的区别:
总结:
**虚拟存储系统:**辅存中的数据并不是一次性全部加载到主存中的,而是和 Cache——主存 类似,根据局部性原理加载一部分数据到主存中。
举例:打游戏时的“Loading”界面背后可能就是在 将游戏地图相关数据调入内存中。
和Cache、主存类似,辅存中数据也被分为一个一个的块。
为了实现只加载一部分数据到主存中,需要对页表补充一些信息。
逻辑页号:逻辑地址中的页号
主存块号:物理地址中的主存块号
外存块号:辅存中对应的块号
有效位:这个页面是否被调入主存中
访问位:用于页面替换算法,主存也有用完的时候,需要进行替换,例如可以使用最不经常使用算法,统计访问次数。
脏位:这个页面是否被修改过,修改主存后会导致主存和辅存之间数据不一致。
由于主存和辅存之间的数据调入是由操作系统来调控的,因此具体细节请看操作系统相关内容,以上仅做粗略介绍。
按照功能模块进行拆分。
例如:#0段是自己的代码,#1段是库函数的代码,#2段是变量。
和页式存储类似,操作系统以段为单位来决定哪一些段应该被调入内存中。
把程序先按功能模块分段,每段再划分为固定大小的页,主存空间也划分为大小相等的页。
程序对主存的调入,调出仍然以页为基本传送单位,每个程序对应一个段表,每段对应一个页表。
虚拟地址:段号 + 段内页号 + 页内地址。
具体详情请看操作系统内容。