第三部分:存储器层次结构

3.1 存储器概述

存储器的分类

  1. 按存储介质分类

    • 半导体存储器:如内存(DRAM)、Cache(SRAM)、ROM。速度快,易失(RAM)或非易失(ROM)。
    • 磁表面存储器:如硬盘、磁带。非易失,容量大,速度慢。
    • 光存储器:如CD、DVD、蓝光。非易失,速度慢。
  2. 按存取方式分类

    • 随机存取存储器(RAM):访问任意地址时间相同(内存、Cache)。
    • 只读存储器(ROM):只能读不能写(或需要特殊方式写)。
    • 串行存取存储器:如磁带,顺序访问。
    • 直接存取存储器:如硬盘,先定位到磁道再顺序查找扇区。
  3. 按信息可保存性分类

    • 易失性存储器:断电后信息丢失(RAM)。
    • 非易失性存储器:断电后信息保持(ROM、硬盘(SSD)、闪存)。

存储系统的层次结构原理

计算机采用多级存储体系,目的是在速度、容量、成本之间取得平衡。从上到下:

  • 寄存器(CPU内部,最快,容量最小,成本最高)
  • 高速缓冲存储器(Cache,SRAM)
  • 主存储器(内存,DRAM)
  • 辅助存储器(硬盘、固态盘等)

存储层次类比:可以把存储系统想象成"办公桌-书架-档案室-仓库"系统。

  • 寄存器 = 桌面上正在用的笔和纸(随手可取,但空间极小)
  • Cache = 桌面的笔筒和小抽屉(常用工具放这里,拿取很快)
  • 主存 = 办公室的书架(存储量大一些,但需要起身去拿)
  • 辅存 = 走廊尽头的档案室(容量巨大,但需要走很久) 这个类比帮助理解:为什么需要多级存储?因为没有任何一种存储介质能同时做到"又快又大又便宜"。

各级存储器速度对比(以具体数字帮助理解差距):

存储层次 典型容量 典型访问时间 相对差距
寄存器 几十~几百字节 ~0.3ns 1x
L1 Cache 32~64KB ~1ns ~3x
L2 Cache 256~512KB ~4ns ~13x
L3 Cache 8~32MB ~15ns ~50x
主存(DRAM) 8~64GB ~80ns ~267x
SSD 256GB~2TB ~10us(10000ns) ~33000x
机械硬盘 1~8TB ~10ms(10^7ns) ~33000000x

这个表格说明:CPU访问寄存器和访问硬盘的速度差距高达数千万倍。如果没有Cache,CPU大部分时间都在等待数据。

局部性原理(存储层次能够有效工作的基础)

  • 时间局部性:如果一个信息被访问,那么不久很可能再次访问它(如循环体)。
  • 空间局部性:如果一个信息被访问,那么它附近的信息也可能很快被访问(如数组顺序访问)。

利用局部性,可以把当前常用的数据和指令从慢速存储器复制到快速存储器中,CPU大部分时间访问快速存储器,从而获得接近快速存储器的速度和接近大容量存储器的容量。

局部性原理的具体例子:

// 时间局部性的典型例子:循环
for (int i = 0; i < 1000; i++) {
    sum += a[i];  // sum变量在每次循环中都被访问,体现了时间局部性
}
// 空间局部性的典型例子:数组顺序访问
// a[0], a[1], a[2], ... 在内存中是连续存放的,体现了空间局部性

反例(缺乏局部性):

// 跳跃访问破坏空间局部性
for (int i = 0; i < 1000; i++) {
    sum += a[random_index[i]];  // 随机访问,无法利用空间局部性
}

💡 记忆技巧:

  • 时间局部性 = "刚用过,马上又用"(循环中的循环变量)
  • 空间局部性 = "用了这个,旁边的也快用了"(数组的顺序访问)
  • 存储层次原则:"速度越快越贵越小,越靠近CPU"

3.2 主存储器(内存)

主存是CPU直接寻址和访问的存储器。考研需要掌握DRAM/SRAM的区别、主存芯片结构、容量扩展方法。

SRAM与DRAM

特性 SRAM(静态RAM) DRAM(动态RAM)
存储单元 触发器(6个晶体管) 电容(1个晶体管+1个电容)
是否需要刷新 不需要 需要(电容电荷会泄漏,约2ms刷新一次)
速度 快(几纳秒) 慢(几十纳秒)
集成度 低(占用面积大) 高(容量大)
功耗 较高 较低
成本 高 低
用途 Cache 主存

💡 记忆技巧:SRAM中的"S"可以联想为"Speed(速度快)",DRAM中的"D"可以联想为"Dynamic(需要动态刷新)"。"S"也是"贵"(贵所以只能做Cache量做小),"D"也是"大"(大容量所以做主存)。

SRAM vs DRAM 对比:SRAM像白炽灯——通电就亮,不需要"刷新";DRAM像手电筒——需要时不时按一下(刷新)保持亮度。

只读存储器(ROM)

  • 掩膜ROM:出厂时写入,不可更改。
  • PROM:可编程一次(用专用设备烧写)。
  • EPROM:紫外线擦除,电编程,可多次。
  • EEPROM:电擦除,可字节级修改。
  • Flash Memory(闪存):块级擦除,广泛用于U盘、SSD。

主存芯片的结构 一个存储器芯片由存储阵列、地址译码器、读写控制电路、数据缓冲器等组成。关键参数:

  • 地址线数量:若地址线为n条,则存储单元数量为2^n(每个单元通常存储1位,但也可多位)。
  • 数据线数量:每次并行读/写的位数(如x1位、x4位、x8位)。
  • 片选信号(CS或CE):用于选择芯片,高电平或低电平有效。
  • 读写控制(WE、OE):WE=0写,WE=1读(通常与OE配合)。

主存容量的扩展 当单个芯片的容量或数据宽度不够时,需要将多个芯片组合。

  1. 位扩展(增加数据宽度)

    • 例如:使用8片 1Kx1位的芯片,组成 1Kx8位的存储器。
    • 方法:所有芯片的地址线、片选信号、读写控制并联,数据线各自独立(分别接数据总线的不同位)。
  2. 字扩展(增加地址范围/容量)

    • 例如:使用2片 1Kx8位的芯片,组成 2Kx8位的存储器。
    • 方法:地址线的高位通过译码器产生片选信号,分别选中不同芯片;低位地址线并联到所有芯片;数据线并联。
  3. 字位扩展(同时增加容量和位宽)

    • 例如:用 1Kx4位的芯片组成 2Kx8位的存储器。
    • 先进行位扩展(两片1Kx4组成1Kx8),再进行字扩展(两组这样的1Kx8组成2Kx8)。

主存扩展完整示例: 题目:用 4Kx4位的SRAM芯片,组成 8Kx8位的存储器,需要多少片芯片?如何连接?

解:

  1. 先位扩展:每组2片4Kx4位组成4Kx8位(数据线由4位扩到8位)。
  2. 再字扩展:需要2组4Kx8位组成8Kx8位(地址范围扩大1倍)。
  3. 总芯片数 = 2 x 2 = 4片。

连接方式:

  • 地址线:A12~A0(2^13=8K)连接到所有芯片。其中A12用于字扩展的片选译码。
  • 第1组(地址 0000~1FFF):A12=0时,通过译码器选中该组。
  • 第2组(地址 2000~3FFF):A12=1时,选中该组。
  • 每组内部,两片芯片的地址线并联,片选信号并联,数据线各自接D0D3和D4D7。

⚠️ 易错点:

  • 地址线数量 vs 地址范围:n条地址线可以寻址2^n个存储单元,每个存储单元不一定是一个字节(可以是1位、4位等)。
  • 位扩展时数据线是"并联还是独立"?——数据线独立(各接总线的不同位),地址线并联(共享地址)。
  • 字扩展时数据线是"并联还是独立"?——数据线并联(共享数据总线),地址线高位通过译码器产生片选。

主存与CPU的连接

  • 需要确定地址总线的位数、数据总线的位数。
  • 分配地址空间:将CPU地址空间的一部分分配给主存芯片,其余可能用于I/O或保留。
  • 地址译码:用门电路或译码器(如3-8译码器)产生片选信号。
  • 时序配合:CPU的读/写控制信号(如MEMR、MEMW)与存储芯片的OE、WE连接。

3.3 高速缓冲存储器(Cache)

Cache是介于CPU和主存之间的小容量快速存储器(SRAM),用于缓解CPU与主存的速度差异。

Cache的基本原理

  • CPU访问主存时,先检查Cache。若数据在Cache中(命中),直接从Cache读取(快);若不在(缺失),则从主存读取一个块(通常几十字节)装入Cache,然后CPU再从Cache读取。
  • 块(Block/Line):Cache和主存之间交换的最小单位,通常包含多个字节。
  • 命中率 = 命中次数 / 总访问次数,缺失率 = 1 - 命中率。
  • 平均访问时间 = 命中时间 + 缺失率 x 缺失开销(从主存读一个块的时间)。

Cache性能计算示例: 假设Cache命中时间为1ns,缺失开销为100ns(从主存读取一个块),缺失率为5%,求平均访问时间。 平均访问时间 = 1ns + 0.05 x 100ns = 1ns + 5ns = 6ns 如果不使用Cache,每次访问都要100ns,使用Cache后平均访问时间降到6ns,性能提升约16.7倍。

另一个示例: 若缺失率降低到1%,则平均访问时间 = 1ns + 0.01 x 100ns = 2ns(性能再提升3倍)。 这说明:Cache的性能主要由命中率决定,提高命中率是Cache设计的核心目标。

Cache的映射方式(决定主存块如何放入Cache)

  1. 直接映射

    • 每个主存块只能映射到Cache中唯一固定的行。
    • 映射公式:Cache行号 = 主存块号 mod Cache行数。
    • 优点:硬件简单,访问速度快。缺点:冲突率高,容易频繁替换。
  2. 全相联映射

    • 主存块可以映射到Cache中任意一行。
    • 优点:冲突率最低。缺点:硬件复杂,查找速度慢(需要比较所有行的标记)。
  3. 组相联映射(折中方案)

    • 将Cache分成若干组,每组包含若干行。主存块先映射到某一组(直接映射),然后在组内任意放置(全相联)。
    • 例如:2路组相联,每组2行。
    • 考研常考:n路组相联,需要理解如何根据主存地址计算组号、标记。

直接映射计算示例: 某Cache有16行(行号0~15),每行存放一个16字节的块。主存地址为32位,问主存块号100映射到Cache的哪一行? Cache行号 = 100 mod 16 = 4(第4行)。

组相联映射完整示例: 某Cache有64行,采用4路组相联(每组4行),每行32字节。主存地址为32位,求:标记、组号、块内偏移各占多少位?

解:

  • 块内偏移:每行32字节 = 2^5,所以偏移占5位。
  • 组数:64行 / 4路 = 16组 = 2^4,所以组号占4位。
  • 标记:32 - 4(组号) - 5(偏移) = 23位。

所以主存地址格式为:标记(23位) + 组号(4位) + 块内偏移(5位)。

Cache的结构 每行(Cache行)包含:

  • 有效位(valid):表示该行是否包含有效数据(初始为0)。
  • 标记(tag):用于判断该行存放的是哪个主存块(高位地址)。
  • 数据块(block):实际存储的数据(多个字节)。

Cache的读操作(以组相联为例)

  1. 将主存地址分解为:标记(Tag)、组索引(Index)、块内偏移(Offset)。
  2. 根据Index找到对应的Cache组。
  3. 在组内并行比较所有行的标记是否与Tag相等且有效位为1。
  4. 若相等且有效,命中,根据偏移取出数据。
  5. 若不命中,从主存读取整个块装入Cache的一行(如果该组已满,则按替换算法淘汰一行)。

Cache的替换算法(当需要装入新块且组内无空闲行时)

  • 随机法:随机选一行替换。
  • FIFO(先进先出):替换最早装入的那一行。
  • LRU(最近最少使用):替换最长时间未被访问的行。考研常见,需要理解计数器或栈实现思路。
  • LFU(最不经常使用):替换访问次数最少的一行(较少考)。

LRU替换算法示例: 一个2路组相联Cache(每组2行),初始为空。某组被访问的主存块序列为:A, B, C, A, B, C,使用LRU替换算法,问最终该组中存放的是哪些块?

访问 行0 行1 替换操作
A A(新) 空 A装入行0
B A(旧) B(新) B装入行1
C A(旧) C(新) 替换B(C是新访问)
A A(新) C(旧) A命中,更新为最新
B B(新) C(旧) 替换A(A是最近最少使用)
C B(旧) C(新) C命中

最终:行0=B,行1=C(或B, C都在)

Cache的写策略(当CPU写数据时)

写操作比读操作复杂,因为要保证主存和Cache的一致性。

  • 写直达(Write Through):同时写Cache和主存。优点:简单,主存始终最新;缺点:慢(每次写都要访问慢速主存)。

  • 写回(Write Back):只写Cache,当该行被替换时才写回主存。需增加一个 脏位(dirty bit) 标记该行是否被修改过。优点:快,减少主存写次数;缺点:实现复杂。

  • 写分配(Write Allocate):写缺失时,先将缺失块从主存读到Cache,再写Cache(通常与写回配合)。

  • 非写分配(No-Write Allocate):写缺失时,直接写主存,不调入Cache(通常与写直达配合)。

考研中常考:写直达+非写分配;写回+写分配。

写策略类比:

  • 写直达 = "每次记笔记都同时抄到正式笔记本上"(多花时间但保证正式本最新)
  • 写回 = "先写在草稿纸上,最后再整理到正式笔记本"(快但需要记得最终整理)

多级Cache 现代CPU有L1、L2、L3 Cache。L1最快最小,通常分为指令Cache和数据Cache(哈佛结构)。L2、L3更大更慢。平均访问时间公式可扩展。

多级Cache性能示例: 假设L1 Cache命中时间=1ns,命中率=90%;L2 Cache命中时间=10ns,命中率=95%;主存访问时间=100ns。求平均访问时间。

解:

  • L1缺失后,访问L2,L2命中的情况:L1缺失率=10%,L2命中率=95%
  • L2也缺失后,访问主存:L2缺失率=5%
  • 平均访问时间 = 1ns + 0.10 x (10ns + 0.05 x 100ns)
  • = 1ns + 0.10 x (10ns + 5ns) = 1ns + 0.10 x 15ns = 1ns + 1.5ns = 2.5ns

💡 记忆技巧:

  • 三种映射方式的对比:"直接映射像固定座位(冲突多),全相联像自由选座(冲突少但找座慢),组相联是折中"
  • LRU替换口诀:"找最久没用的替换"
  • 写策略组合:"写直达+非写分配"是一对,"写回+写分配"是一对,两者通常配对使用。

⚠️ 易错点:

  • Cache容量和"行大小"的区别:Cache容量 = 行数 x 每行字节数。不要混淆。
  • 直接映射中,主存块号 mod Cache行数,这个mod的结果是行号,不是块内地址。
  • 组相联中"路数"的含义:2路组相联 = 每组2行,不是组数为2。

3.4 虚拟存储器

虚拟存储器是操作系统与硬件配合实现的"内存扩充"技术,让每个程序认为自己拥有独立的连续地址空间(虚拟地址),而实际数据可能存放在主存或硬盘上。考研中,这部分与操作系统课程有交叉,但组成原理主要关注地址转换和TLB。

基本概念

  • 虚拟地址(逻辑地址):程序使用的地址。
  • 物理地址:主存中的实际地址。
  • 页:虚拟地址空间和物理地址空间都被划分为固定大小的块(通常4KB)。
  • 页表:记录虚拟页到物理页框的映射关系,以及访问权限、有效位等。

虚拟存储器类比:可以把虚拟存储器想象成"酒店房间号"系统。

  • 程序使用的是"房间号"(虚拟地址),如808号房。
  • 实际上808号房可能在酒店的8层(物理地址),也可能客人还没到(不在主存中,在硬盘上)。
  • 前台(MMU+页表)负责把"房间号"映射到实际的房间位置。
  • 如果客人还没入住(缺页),就需要从"候补名单"(硬盘)调入房间(内存)。
  • TLB就像前台桌上的"常用房间映射快查表",不用每次都去翻大册子(内存中的页表)。

页式虚拟存储器的地址转换

  • CPU发出虚拟地址,分为虚拟页号(VPN)和页内偏移。
  • 用VPN索引页表,得到物理页框号(PPN)。
  • 物理地址 = (PPN << 页内偏移位数) + 偏移。

地址转换示例: 假设页面大小为4KB(2^12=4096字节),页内偏移占12位。某程序访问虚拟地址 0x12345678。

  • 虚拟页号 VPN = 0x12345(高20位)
  • 页内偏移 = 0x678(低12位)
  • 通过页表查得物理页框号 PPN = 0xABCD
  • 物理地址 = (0xABCD << 12) + 0x678 = 0xABCD000 + 0x678 = 0xABCD678

快表(TLB,Translation Lookaside Buffer)

  • 页表存放在主存中,每次地址转换需要访问主存(慢)。TLB是CPU内部的一个小Cache,用于存放最近使用的页表项。
  • 工作过程:CPU先查TLB,若命中(TLB hit),快速得到物理地址;若未命中(TLB miss),则访问主存中的页表,并将该页表项装入TLB(可能替换旧项)。

TLB与Cache的关系(408常见综合题): 在一条指令的执行过程中,数据可能经过以下路径:

  1. CPU发出虚拟地址
  2. 查TLB,将虚拟地址转换为物理地址(TLB命中则快,不命中则查页表)
  3. 用物理地址查Cache(Cache命中则快,不命中则查主存)
  4. 若Cache和TLB都不命中,则需要访问主存2次(一次查页表,一次读数据)

缺页处理

  • 如果页表项的有效位为0(该页不在主存中),则触发缺页异常,操作系统从硬盘读取该页到主存,更新页表,然后重新执行指令。

段式与段页式(简要了解)

  • 段式:按逻辑结构(代码段、数据段、堆栈段)划分,每段大小可变。地址转换需段表(基址+限长)。
  • 段页式:先分段,每段内再分页。

考研中,组成原理主要考页式虚拟存储器的地址转换、TLB、有效位等。

跨学科联系(操作系统):虚拟存储器是组成原理和操作系统高度交叉的内容。在操作系统中你会学到:

  • 页面置换算法(LRU、FIFO、Clock等)
  • 页表的具体实现(多级页表、反向页表)
  • 缺页中断的处理流程
  • 虚拟内存管理的性能分析(有效访问时间公式)

3.5 存储器与CPU的连接(回顾与补充)

前面已讲主存扩展和连接,这里补充一个完整例子:

假设CPU有16位地址总线(A15A0)和8位数据总线,需要用 8Kx8位的RAM芯片组成 32Kx8位的存储器,并给出地址分配(例如 0x00000x7FFF 为RAM)。

  • 需4片芯片(32K/8K=4)。
  • 使用2-4译码器,用高2位地址(A15、A14)产生片选信号。
  • 低13位地址(A12~A0)直接连接到所有芯片的地址引脚(8K=2^13)。
  • 数据总线、读写控制并联。

地址空间分配:

芯片编号 A15 A14 地址范围 片选条件
芯片0 0 0 0x0000~0x1FFF A15=0, A14=0
芯片1 0 1 0x2000~0x3FFF A15=0, A14=1
芯片2 1 0 0x4000~0x5FFF A15=1, A14=0
芯片3 1 1 0x6000~0x7FFF A15=1, A14=1

连接图示说明: A15, A14 -> 2-4译码器的输入 译码器输出 Y0Y3 -> 分别接芯片03的CS引脚 A12A0 -> 所有芯片的地址引脚 D7D0 -> 所有芯片的数据引脚 WE -> 所有芯片的WE引脚


📌 408考点提示

考查形式:

  • 选择题高频:Cache映射方式(给定地址求组号/标记/块内偏移)、主存扩展计算芯片数、SRAM/DRAM对比。
  • 综合题常考:Cache的性能计算(平均访问时间)、Cache和TLB的联合工作过程。
  • 简答/设计题:主存扩展的电路连接(画图或描述连接方式)。

常见命题模式:

  1. 给定主存地址位、Cache参数(行数、路数、块大小),求地址划分,计算Cache容量。
  2. 给定命中率、命中时间、缺失开销,计算平均访问时间。
  3. 给定程序访问序列,分析Cache命中情况(用LRU或FIFO替换)。
  4. 主存芯片扩展题:给芯片规格和目标规格,要求计算芯片数和画出连接图。

学生常犯错误:

  • Cache地址划分时弄混组号和标记的位数。记住:先确定块内偏移(由块大小决定),再确定组号(由组数决定),剩下的位数是标记。
  • 平均访问时间公式忘记乘以缺失率(只算了缺失开销,忘了缺失率这个权重)。
  • LRU替换时维护访问顺序出错(常见于2路组相联的判断)。
  • 主存扩展时地址线的连接弄错(位扩展和字扩展的地址线连接方式不同)。

备考建议:

  • Cache地址划分必须熟练掌握,建议做10道以上练习巩固。
  • 熟记平均访问时间 = 命中时间 + 缺失率 x 缺失开销,这个公式在408中反复出现。
  • LRU替换算法务必动手模拟3~5个例子,理解"最近最少使用"的准确含义。
  • 主存扩展题最关键的是判断先位扩展还是先字扩展,以及片选信号的产生方式。

第三部分小结

主题 核心要点
存储层次 寄存器->Cache(SRAM)->主存(DRAM)->辅存;局部性原理
主存扩展 位扩展、字扩展、字位扩展;地址译码
Cache映射 直接、全相联、组相联;标记、索引、偏移
Cache替换 FIFO、LRU、随机;写策略(写直达/写回,写分配/非写分配)
虚拟存储器 页式管理,虚拟地址->物理地址转换,页表,TLB,缺页异常