Bootstrap

计算机组成原理基础知识-存储系统

整体框架:

(1)关于存储器的分类、一些性能指标、层次化的存储系统

(2)SRAM、DRAM、ROM

(3)存储器的特殊结构

(4)主存储器

(5)Cache

(6)虚拟存储器

1.三级存储系统:

cache-主存-辅存,cache和主存之间主要解决速度问题(通过两级cache或者多级cache的这种层次化的结构来实现速度的提升),主存和辅存主要解决容量问题。

2.SRAM、DRAM、ROM

对于SRAM来说,实际内部的结构是触发器,因此对于读写实际是可以稳定的。而对于DRAM来说,内部实际上是电容,对其进行写操作实际上是对电容进行充放电,对其进行读操作实际上是通过特殊的检测电路检测电容的电流变化,因此对于DRAM来说其读操作实际是破坏性的读出。并且由于电容的特点,DRAM实际是需要进行刷新操作以保证其信息不会改变。

为了减少片选线的数量,因此采用行列地址的方式,也就是说系统中存在两个译码器(这种方式可以减少片选线的数量,比如一个八位的地址,仅仅一个译码器的化需要2**8根线,但是对于行列分开的情况下仅仅需要2*2**4根线)。对于SRAM来说,送行列地址采用的是同时送,而对于DRAM来说,采用的是分两次送(先送行地址,然后把其放在内部一个位置进行保存,再送列地址,两个地址合并得到真正地址),因此DRAM可以节约地址用线。

对于DRAM来说其刷新的方式包含集中刷新、分散刷新、异步刷新。刷新的过程是针对行地址进行刷新的,也就是一次刷新刷新一行所有的存储单元。一般对于DRAM来说,刷新周期是2ms,对于集中刷新来说,就是在一个刷新周期内利用一段固定的时间对DRAM中所有行进行刷新,这段时间称为死区,死区的存在使得在一端固定时间内是无法访问存储单元的,但是其读写周期不受刷新工作的影响。对于分散刷新,是在每次读写完成后都进行刷新一行,这种操作虽然没有死区的存在,但是增加了系统的存取周期。异步刷新结合了前面两种方式,刷新周期(2ms)除行数等到两次刷新操作之间的间隔t,也就是每隔t进行刷新一次,这种操作方式任然是存在死区的,但是这个死区是分散的,可以通过合理的指令安排在死时间的时候不去访问存储器

SRAM和DRAM均是掉电信息就会丢失,而对于ROM来说即使掉电信息也不会丢失(只读)。

3.存储器的特殊结构

在针对存储器进行操作时存储周期等于存取时间+恢复时间,因此一次存储结束后并不是立马就可以进入到下一次的存储,需要有一段恢复时间。存储器的特殊结构就是针对这段恢复时间,优化存储器使得其能够利用到这段时间。

双端口结构:双端口RAM。

多模块结构:多体并行存储器(既能并行工作,又能交叉工作)

包含两种模式,第一种是高位交叉编址第二种是低位交叉编址。高位交叉编址实际上是通过高位体号确定存储体,然后通过低位体内地址进行译码,得到单个体内具体的地址,这种方式由于前后操作均是在一个存储体中,因此实际上并不能利用到恢复时间。针对低位交叉编址,实际上是通过低位的体号确定具体的存储体,在通过高位体内地址进行译码,得到各个存储体之间需要访问的地址(在同一行的地址),这种操作由于前后访问的存储体并不是同一个存储体,因此可以将恢复时间利用起来。

4.主存储器

(1)基本构成(2)与CPU的连接(3)容量的扩展:位扩展法、字节扩展法、字位同时扩展法

(4)存储器的地址分配:线选法、译码片选法

5.Cache

一些基本的点:为了便于Cache和主存之间交换信息,Cache和主存都被划分成相等的块,Cache块称为Cache行(cache line),每块由若干字节组成,块的长度称为块长,Cache中的块数是小于主存之间的块数,并且Cache与主存之间的信息交互是以块为单位,而CPU与Cache的信息交互是以字节为单位

CPU访问内存是非常慢的,所以我们在CPU中增加了3级缓存,并且每次CPU从内存中读取数据的时候是一次读一个cache line到 cache中以提升效率,一般情况下cache line的大小是64 byte(这里实际操作的时候涉及一个正常的byte地址转化为块地址的操作),也就是每次读取64byte到CPU cache中。Cache Line 是 CPU 和主存之间数据传输的最小单位。当一行 Cache Line 被从内存拷贝到 Cache 里,Cache 里会为这个 Cache Line 创建一个条目。

局部性原理:程序访问的局部性原理包含时间局部性和空间局部性。时间局部性是最近未来要用到的一些信息很可能是现在正在使用的信息(比如一些程序的循环中),空间局部性是指最近未来要用的信息可能是处于当前所在信息的周围位置上。

Cache和主存的映射方式(解决主存的块放在Cache中的哪个位置的问题):

全相联映射:主存中的每一块地址可以随意的放在Cache中的任何位置,主存的地址编码是主存字块标记+字块内地址,字块内地址不需要给到cache,只需要把主存字块比较给到cache当作标记,给到Cache的数据是有效位+标记+数据(这个标记是为了区分主存存储到cache中的内容是什么)。全相联映射的优点是灵活性较大,资源利用率也高,但是由于标记位较多,比较的速度较慢,消耗的资源也较大。这种方式仅仅保证了一行内的数据位置是相同的。

直接映射:主存中的每一块只能装入Cache中的唯一位置,如果这个位置有冲突的化,原来的位置被无条件的替换出去(这种方式空间利用率较低并且也容易产生冲突)。比如cache中有八个块,那么主存就按照8为循环,也就是主存中的第一个块和第九个块均是存在Cache中的第一个块。主存的地址结构是主存字块地址+Cache字块地址+字块内地址,主存字块地址便是的是主存中的第几个循环,在前面的例子中表示的就是主存中第几个以八个块为周期的循环,而对于Cache行号表示的一个周期内具体的第几个cache行,给到Cache的数据包含有效位+主存字块地址+数据这种方式不仅仅保证了行内的数据位置相同,cache中行和行之间的顺序也是一致的,因此标记为相较于全相联映射的方式减少了n位(2**n=cache的行数)

组相联映射:将Cache分成相等的周期,主存以此周期为标准,在一个组内Cahce块可以随意放置。比如前面的例子中,将Cache分为两个Cache块为一个周期,那么对于主存来说,也是按照两个Cahce块进行传递,但是主存中的这两个Cache块可以随意的放在Cahce中的该组的任何位置,也就是组间采取直接映射,组内产生全相联映射。主存的地址编码包含主存字块标记地址+组号+字块内地址。需要注意的是,这里实际上是把除了字块内地址以外地址的低位当作组与组之间区分的标记,不使用高位的在于方便将标记为给到Cahce以便于区分,而主存字块标记地址表示的是同一个组内不同的Cache行。由于组与组之间的排序是按照顺序的(直接映射),因此组号不需要给到Cache,而组内的地址排序是随机的(全相联映射),因此,需要标记进行区分,因此需要把主存字块标记地址给到Cache。组相联映射如果分组的组数位一个cache行,那么就是直接映射,如果分组的组数是n行,n为cache的总共行数,那么就变化为全相联映射。

Cache中主存块的替换算法:

替换算法仅仅针对于全相联和组相联的映射方式,也就是这两种方式在cahce被占满的时候是需要一定的算法来实现究竟替换的是哪个cahce块,而对于直接映射每个cache块在cache中都由固定的位置,因此不需要额外的算法实现。

随机算法:随机的选择一个cache块进行替换(RAND);先进先出算法:选择最早进入cache块进行替换(FIFO,没有考虑局部性原理);近期最少使用的算法:依据局部性原理,选择近期内长久没有访问的cache块进行替换(LRU,对每一行设置一个计数器,如果这个cache被命中的化,这个计数器就清零,否则就继续加一,在实际替换的时候选择计数值最大的那个行进行替换);最不经常使用算法:将一段时间内访问最少的进行替换(LFU,这方式的实现是对cache的每一行设置一个计数器,每次被命中一次就会加一,最终替换的时候看谁的值最小)。

注意近期最少使用和最不经常使用之间的区别。

关于LRU和LFU的对比:对于LRU实际的操作是对每行设置一个计数器,只要命中的化,就会清0,同时其他加1,在进行替换的时候选择计数器最大的进行替换;而对于LFU实现的方式是同样设置一个计数器,每命中一次,计数器加一,替换的时候选择计数器值最小的替换。从这里可以对比,LRU强调了近期的概念,即使一行很久没有命中,只要下次他命中的化就会被清0,替换的时候就不会被替换,而对于LFU更加强调的是整体的概念。

Cache写策略(解决Cache的内容修改之后如何保存与主存中的内容相同的问题):

对于cache的写命中的情况下,有两种情况,第一种是把数据同时写入cache和主存(全写法)。但是数据写入主存的速率往往是较慢的(除此以外还存在增加了访问次数,降低了cache的效率等问题),因此为了解决速度不匹配的问题,减少全写法直接写入主存的时间损耗,因此在CPU和DRAM之间加一个write buffer(写缓冲),写缓冲是一个FIFO队列,CPU直接将数据写到写缓冲内部去,再由写缓冲将内容写道主存中。全写法很好的解决了cache和主存数据不一致的问题,但是出现频繁的写时,会出现写缓冲饱和溢出的问题(这种方式不需要设置脏位)。第二种时写回法CPU命中Cache时,仅仅将cache中的内容改变,而不修改主存中的内容,只有当此块被换出的时候才会写回主存。为了判断Cache中此块的内容是否被修改过,因此需要增加一个脏位来判别Cache中的此块内容是否被写过,在数据被提出cache的时候根据脏位是否为1来判断是直接将数据覆盖还是需要将数据写道主存中。这种方式减少了访问的次数,但是存在Cache中的数据和主存中的数据不一致的问题

对于Cache没有命中的情况下也存在两种解决方式,第一种是写分配法,也就是再没有写命中的时候把相应的块调入到cache中,然后CPU对Cache进行写操作(命中的时候写回法,没有命中的时候写分配法)。这种方式需要搭配前面的写回法来实现,也就是需要设置脏位,再Cache被踢出的时候根据脏位来判定是否写回更新主存的内容(这种方式考虑了局部性原理)。第二种是非写分配法,这种方式一般搭配全写法,也就是直接把数据写到主存中,需要搭配write buffer使用

基于此,可以通过设计两级Cache的系统解决这两者的缺点,也就是CPU与第一级cache通过全写法进行操作,这两者之间存在write buffer;而第一级cahce与第二级cache之间采用写回法(增加了一个cache,也就是再多一个第二级的cache以后才到DRAM),由于第二级cache的存在,write buffer的传输从原来的直接到DRAM变为到第二级cache,速率提高,因此避免了因为频繁的写造成的写缓冲饱和溢出(对于cache来说,离cpu越远速度越慢,容量越大)。

6.虚拟存储器

(1)以页为基本单位

解决虚页号到实页号的映射关系:通过页表,页表中存放了物理页号和装入位,装入位表示的是虚拟地址所指向的数据是在主存中还是在辅存中(1表示在主存中,0表示在辅存中)。如果标志位为1那么直接将物理页号和页内地址进行拼接,根据得到的地址到主存中去访问相关地址获取数据。如果标志位为0,那么就需要操作系统将这一页调入主存中,再继续完成上述的虚拟地址到物理地址的映射。

如何根据虚页号找到对应的实页号:实际上是通过一个页表基址寄存器来实现的,页表基址寄存器中存放了页表的起始地址,起始地址和虚页号拼接就会得到虚页号所对应的实页号的页表地址(这个地址称为页表项地址),把这个地址中所存的实页号取出和页内地址进行拼接就会得到需要访问的数据所对应的主存地址。对于这个主存地址是通过访问主存-cache体系来实现的,也就是这个地址在cache中就直接访问,如果不在cache中就进行访问主存。

需要注意的是,虚拟地址实际是不存在在主存中的,但是页表实际是在主存中的,因此页表基地址和物理地址实际都是主存地址。

(2)以段为基本单位

原有的页表替换成了段表。段和页的区别是页的大小是固定的,主存和虚存都是分为大小相等的页,而对于段来说,其大小是不固定的,主存和虚存不需要提前拆分成大小相同的页。此时虚拟地址还是分为段号和段内地址,但是物理地址不在划分出段号,映射关系还是相同的,也就段号和段表基址寄存器中的段表起始地址拼接得到相应段表中的内容,将这个内容和段内地址相加得到主存地址(这个地址就是虚地址所对应的物理地址)。

(3)段页式

将每个段划分成相同大小的页。程序调入调出时任然以页为基本单位。虚地址包含段号、段内页号、页内地址;实地址则是由页号和页内地址合并得到。

(4)快表

页表和段表都是存储在主存中的,称为慢表(Page,访问速率较慢),而存储在高速缓冲器中的表称为快表(TLB)。Cache是主存的副本,快表是慢表的副本。在进行执行的时候是块表和慢表同时进行执行,快表的访问速度往往较快,可以较快的判断其是命中,如果没有命中就要看慢表是否命中,如果慢表也没有命中,则需要操作系统进行页面调度。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;