深入理解计算机系统
第六章——存储器层次结构
存储技术
随机访问存储器(RAM)
- 随机访问存储器分为静态存储器(SRAM)和动态存储器(DRAM)
每位晶体管数 | 相对访问时间 | 持续的? | 敏感的? | 相对花费 | 应用 | |
---|---|---|---|---|---|---|
SRAM | 6 | 1x | 是 | 否 | 1000x | 高速缓存存储器 |
DRAM | 1 | 10x | 否 | 是 | 1x | 主存、帧缓冲区 |
- 传统的DRAM
-
DRAM中的芯片的单元(位)被分为
d
个超单元,每个超单元都由w
个DRAM单元构成。一个d
xw
的DRAM共存储了dw
位信息。超单元被组织成一个r
行c
列的长方形阵列,这里rc
=d
。每个超单元有形如(i,j)的地址,分别代表行和列。 -
如图6-3所示, DRAM芯片连接到内存控制器。当DRAM与内存控制器数据传输时,假定需要读取DRAM超单元(i,j)内容。首先内存控制器将那个行地址i发送给DRAM,然后再把列地址j发送给DRAM。行地址i分为RAS,列地址j分为CAS。特别注意的是,RAS与CAS请求共用同一个DRAM地址引脚。
-
下面6-4是内存控制器读取DRAM更加细节的部分,读取图6-3中(2,1)超单元部分,首先发送行地址i=2,即把第2行的整个内容复制到内部行缓冲区,之后再发送列地址j=1,DRAM接收后,把超单元(2,1)里面的8个字节通过data引脚发送内存控制器。
-
这是一个值得思考的问题,为什么DRAM内部是采用二维阵列,而非线性数组。
- 还是假定是128 (128==16x8) 位的DRAM。
- 如果采用线性数组的形式,则需要16个 超单元的线性数组,地址引脚则需要4个地址引脚( 16 = 2 4 16=2^4 16=24)。
- 而对于二维阵列而言,如上所示,我们只需要两个地址引脚(行地址引脚和列地址引脚)即可。
-
内存模块
- DRAM封装在
内存模块
中,它插到主板的拓展槽内。-
其中Core i7采用240个引脚的双列直插内存模块,以64位为块传送数据。
-
图6-5展示了一个内存的基本思想。模块由8个64 Mbit的8Mx8的DRAM芯片构成,一共存储64MB。每一个超单元存储主存的一个字节,最后使用响应的超单元地址(i,j)来表示主存中的A处的64位字。
-
需要取得内存地址A的一个字时,内存控制器会将地址A转换为一个超单元地址(i,j),最后通过广播每一个(i,j)DRAM超单元得到拼接后的字。
-
- DRAM封装在
-
增强的DRAM**(拓展部分)**
- 快页模式DRAM 如上述读取DRAM一个超单元时,读取到行内部缓冲区时,一直进行保存,这样同一行的列内容可以快速得到
- 拓展数据输出DRAM
- 同步DRAM
- 双倍数据速率同步DRAM,使用两个时钟沿作为控制信号,从而使DRAM速度翻倍。在购买电脑内存的时候,有时说的DDR DDR2 DDR3就是根据这个而来的。 Intel Core i7 只支持DDR3 SDRAM
- RAM
-
非易失性存储器(ROM)
- “易失”这里指的是断电后是否会失去它们的信息,上述提到的SRAM、DRAM都是“易失”的。
- 由于历史原因ROM有的类型既可以读也可以写,但是大部分来说,目前都是只可以读的。
- ROM分类(根据能被重新编程的次数和对它们进行重编程所用的机制来区分)
- PROM
- 可擦写可编程ROM EPROM EEPROM
- 闪存
- ROM通常也称为“固件”
- 提供少量输入输出函数——BIOS(重装系统的同学会比较熟悉)
- 一些复杂设备依赖来之CPU的I/O固件翻译,比如图形卡,磁盘驱动控制器
-
访问内存
-
总线是一组并行的导线,能携带地址、数据、控制信号。
-
move A,%rax
把地址A的内容加载到寄存器%rax
- CPU将地址A加载到系统总线——>I/O桥将信号传递到内存总线——>主存读取信号,从DRAM读出数据,写到内存总线——>反方向运行——>到达系统总线时,读取数据到rax。
-
move %rax, A
把寄存器rax的内容传送到地址A
- CPU将A地址放入系统总线
- 内存总线通过I/O桥读取系统总线数据,DRAM等待数据传输
- CPU将%rax数据沿方向传入DRAM中
-
磁盘存储
-
磁盘构造
-
磁盘由盘片构成,盘片由2个表面构成。
-
结构如下
-
磁盘——盘片——盘面(表面)——磁道——扇区
-
扇区一般由512字节构成,扇区之间有gap,不存储数据位,用来存储用来标识扇区的格式化位。
-
磁盘也称之为 磁盘驱动器,简称磁盘。这里我们也称之为旋转磁盘,与基于内存的固态硬盘区分
-
-
-
磁盘容量
-
磁盘可以记录的最大位数称为它的最大容量,简称容量。由以下因素决定:
- 记录密度:一个磁道一英寸可以放入的位数
- 磁道密度:从盘面中心出发,一英寸包含的磁道数
- 面密度:记录密度与磁盘密度的乘积
-
在之前磁盘开始设计时,往往要求所有磁道存储信息容量相同,但是这样会导致外圈的面密度较低。现代采用的是多区记录——柱面的集合分成不相交的子集合,也称之为记录区。
-
磁盘空间记录公式如下(公式与结构一一呼应):
-
-
磁盘操作
-
磁盘采用读/写头来读写存储在磁性表面的位,而读/写头连接在一个传动臂一端。传动臂沿半径移动寻找目标磁道过程称之为寻道。一旦定位到目标磁道,读写头可以感知这个位,也可以修改这个位。特别的,在任何时候,所有的读写头都位于同一个柱面上。如图6.10所示:
-
传动臂末端的读写头在移动过程中,如果碰到一粒灰尘影响也很大,会停下来撞到盘面——即读/写头冲撞。因此,磁盘总是密封保存的。
-
磁盘读取数据是以扇区大小的块为单位读取。对扇区的访问时间由以下三个主要部分构成
磁盘读取数据时间=寻道时间 + 旋转时间 + 传送时间
磁盘访问过程中主要时间在寻道时间和旋转时间,其他时间都可以忽略不计。
-
-
逻辑磁盘块
上述的磁盘访问过程如果由程序员专门控制的话,简直会累死人。有幸,为了隐藏现在磁盘的复杂性,现在磁盘将它们的构造呈现为一个简单的视图,一个B个扇区大小的逻辑块序列,编号为0,1,2 … , B-1。磁盘中封装由一个小的硬件/固件设备,称之为磁盘控制器。
磁盘控制器主要用于维护逻辑块号和实际(物理)磁盘扇区的映射关系。
过程如下:
- 操作系统执行I/O操作时,需要读取磁盘的数据到主存中。操作系统发送命令给磁盘控制器,让它读取某个逻辑块号。
- 控制器上的固件执行一个快速表查找,将一个逻辑块号翻译为(盘面,磁道,扇区)唯一标识对应的物理扇区。
- 得到物理扇区后,将读/写头移动到某个柱面,等待扇区移动读取数据,存储在控制器的一个小缓冲区中,将他们复制到主存中。
-
连接I/O设备
之前提及到系统总线和内存总线连接CPU、内存等数据、指令、控制信息的传输。I/O总线可以容纳种类繁多的第三方I/O设备。例如USB、图形卡、主机总线适配器、网络适配器等。
-
访问磁盘
-
PCI中所有设备共享总线,一个时刻只能由一台设备访问这些线路,目前已经被PCIe总线替代,PCIe == PCI express
-
CPU使用称为内存映射I/O技术来向I/O设备发射命令,使用内存映射的系统,地址空间由一块是为I/O设备通信而保留的。其中的地址,我们称之为I/O端口,当一个设备连接总线总线时,他与一个或多个端口相关联。
-
如图a、b、c中所示,假设磁盘控制器映射端口0xa0。CPU向地址0xa0可能发起三个存储指令:
- 发送磁盘读取命令
- 指明应该读取的逻辑块号
- 指明存储在主存中的端口地址
发送指令读取成功后,磁盘数据返回主存而不需要CPU干涉。自己执行读写总线事务而无需CPU干涉称之为直接内存访问(DMA),这种数据传输称为DMA传送。
当数据成功传输到主存时,磁盘控制器发送中断信息通知CPU完成。
-
固态硬盘——SSD
固态硬盘时一种基于闪存的存储技术,其行为和其他硬盘一样,都是处理CPU发送的逻辑块的请求。一个SSD封装由一个或多个闪存芯片和闪存翻译层构成。
其中闪存芯片代替传统的机械驱动器,闪存翻译层与磁盘控制器的功能类似。
SSD中随机写比较慢,原因如下:
- 写的过程需要擦除块
- 如果写入一个具有数据的页,必须把其所在的块复制备份到其他块,才可以继续写。
存储技术趋势
发展过程中,CPU的发展幅度速度远远大于主存、磁盘速度。近些年来,CPU的发展因功耗太大,不能像之前一样迅速增加时钟频率,进而使用多个小核处理器。
局部性
一个良好的程序往往需要局部性,局部性分为时间局部性和空间局部性。
- 重复引用相同变量的程序具有时间局部性
- 对于步长为k的引用模块程序,步长越小,空间局部性就越大
- 对于取指令来说,循环具有良好的时间和空间局部性。
存储器层次结构
层次结构如下:
存储器层次结构的存储
层次结构的中心思想是,对于每个k层的更快更小的存储设备作为位于k+1层的更大更慢的存储设备缓存。第k层的数据往往是第k+1层的子集。
由于随着较低层设备访问时间较长,为了弥补这些较长的访问时间,倾向于使用较大的块。
缓存类型 | 缓存内容 | 缓存位置 | 延迟(时钟周期) | 管理者 |
---|---|---|---|---|
寄存器 | 4-8 字节的字 | CPU 内核 | 0 | 编译器 |
TLB | 地址翻译 | 芯片 TLB | 0 | 内存管理单元 |
L1 缓存 | 64 字节的块 | 芯片 L1 缓存 | 4 | 硬件 |
L2 缓存 | 64 字节的块 | 芯片 L2 缓存 | 10 | 硬件 |
虚拟内存 | 4 KB 的页 | 主存 | 100 | 硬件+操作系统 |
缓冲区缓存 | 文件的部分内容 | 主存 | 100 | 操作系统 |
磁盘缓存 | 磁盘扇区 | 磁盘控制器 | 100,000 | 磁盘固件 |
网络缓冲区缓存 | 文件的部分内容 | 本地磁盘 | 10,000,000 | NFS 客户端 |
浏览器缓存 | 网页 | 本地磁盘 | 10,000,000 | 网络浏览器 |
Web 缓存 | 网页 | 远程服务器磁盘 | 1,000,000,000 | Web 代理服务器 |
高速缓存存储器
通用的高速缓存存储器组织结构
假定CS中:
- 存储器由m位构成,可以形成 M = 2 m M=2^m M=2m不同的地址。
- 高速缓存被组织为 S = 2 s S=2^s S=2s个高速缓存组,一个组里有E行高速缓存行。
- 一个行里面由 B B B位数据块,一个有效位,t个标志位构成。标记位唯一表示存储在这个高速缓存行中的块。
- 当CPU需要在主存A中加载数据时,首先检查高速缓冲区中是否A地址缓存行,如果有并且设置了有效位,通过标记位进行验证,如果验证成功,直接取出高速缓冲行的数据。
直接映射高速缓存
直接映射高速缓存的意思是一个组内有且只有一个高速缓存行,即E=1。
高速缓存确定一个请求是否命中,然后抽取被请求的字的过程,分为三步:
-
组选择
从将要访问的地址中提取s个组索引,这些位被解释为一个无符号整数。
-
行匹配
因为一个组只有一行,我们只需要检测有效位和标记位即可。如果有效位已经设置并且标记位匹配,则直接提取数据。
-
直接映射高速缓存的字选择
如果命中了高速缓存,接下来就是取值了。我们通过块偏移来选择需要的字节范围。
如图中,块偏移为100,则我们需要选择4之后的字节即可。
-
不命中时行替换
如果没有命中,那需要从下一层的块取出被请求的块放入到本层的高速缓存行中。如果组里都是高效缓存行,直接暴力替换即可。
-
疑问:为什么组索引不设在高位上,而设置在中间位?
如果组索引设置在高位,我们以数组a[16]打比方,缓冲区只能取到4个组,一个组只有一个缓存行(直接映射高速缓存)。那根据组存储,a[0]——a[3]存储第一组,a[4]——a[7]存储第二组,以此类推。这样的话,往往空间局部性较差。
组相联高速缓存
组相联高速缓存与直接映射高速缓存本质是一样的,不同点在于 E E E的大小大于1,即一个组里面有至少2个缓存行。
如图为 E E E=2的组相联高速缓存示意图:
与直接映射高速缓存行匹配和字选择不同的是,高速缓存必须搜索组里的每一行进行匹配,然后使用标记位进行核对。
全相联高速缓存
-
全相联高速缓存是一个包含所有高速缓存行的组,即只有一个组。
-
其必须并行地搜索许多相匹配的标记,构造一个又大又快的相联高速很难,重点是贵啊。
-
主要用途在于虚拟内存的TLB,用于高速缓存部分地址
有关写的问题
使用高速缓存读取数据过程比较简单,但是对于需要写入数据来说相对复杂。
这里介绍两者写法:
- 直写,不管三七二十一,只要是写入了数据,直接复制到下一层(远离cpu层)进行更新。缺点是总线流量大
- 写回,尽量拖着,不到替换算法要驱逐出该修改模块时,都不写入到下一层。
如何处理写不命中,这样也有两种办法:
- 写分配,每次直接加载到下一层,然后更新这个高速缓存块。对应于写回。
- 非写分配,避开高速缓存,直接把这个字写到低一层。对应于直写
一个真实的高速缓存层次结构的解剖
只缓存数据的高速缓存称之为i-cache,只缓存指令的高速缓存称之为d-cache,既包含指令又包含数据的高速缓存称之为高速缓存。
一个典型的多层高速缓存结构: