4 、存储器
一、存储系统概述🧀
现代计算机通常以存储器为中心。CPU、I/O 设备和外部存储器通过总线与主存交换程序和数据。
1、存储器层次🧀
从靠近 CPU 到远离 CPU,典型存储层次为:
| 层次 | 典型器件 | 速度 | 容量 | 单位成本 |
|---|---|---|---|---|
| 寄存器 | CPU 内部寄存器 | 最快 | 最小 | 最高 |
| Cache | SRAM | 很快 | 较小 | 高 |
| 主存 | DRAM | 中等 | 较大 | 中等 |
| 辅存 | SSD、磁盘 | 较慢 | 最大 | 最低 |
越靠近 CPU,速度越快、容量越小、单位成本越高。存储层次利用程序的时间局部性和空间局部性,在成本可控的前提下尽量接近高速存储器的性能。
2、存储器分类🧀
| 分类依据 | 类型 |
|---|---|
| 存储介质 | 半导体、磁表面、光存储 |
| 存取方式 | 随机存取、顺序存取、直接存取、相联存取 |
| 是否可改写 | 读写存储器、只读存储器 |
| 断电后能否保存 | 易失性、非易失性 |
| 系统层次 | 寄存器、Cache、主存、辅存 |
- 随机存取:任意单元的访问时间与地址无关,如主存。
- 顺序存取:必须按顺序经过前面的记录,如磁带。
- 直接存取:先定位到区域,再在区域内顺序查找,如磁盘。
- 相联存取:根据内容而不是地址查找,如 Cache 中的标记比较。
3、性能指标🧀
| 指标 | 含义 |
|---|---|
| 存取时间 \(T_A\) | 发出访问请求到数据可用所需的时间 |
| 存取周期 \(T_M\) | 连续启动两次独立存储操作之间允许的最短时间,通常 \(T_M\geq T_A\) |
| 存储带宽 | 单位时间内能够传输的数据量 |
若每个存取周期传输 \(W\) 位数据,理想带宽为:
二、主存的组织与编址🧀
1、存储单元、MAR 与 MDR🧀
存储体由许多存储单元组成,每个存储单元又由若干存储元件组成。每个存储单元拥有唯一地址。
MAR:存储器地址寄存器,保存本次访问的地址。MDR:存储器数据寄存器,保存读出的数据或准备写入的数据。
设 MAR 为 \(m\) 位、MDR 为 \(n\) 位,则主存最多包含 \(2^m\) 个 \(n\) 位存储字:
| 单位 | 换算 |
|---|---|
| bit(b) | 最小信息单位 |
| Byte(B) | \(1\,\text{B}=8\,\text{bit}\) |
| KB | \(1\,\text{KB}=2^{10}\,\text{B}\) |
| MB | \(1\,\text{MB}=2^{20}\,\text{B}\) |
| GB | \(1\,\text{GB}=2^{30}\,\text{B}\) |
| TB | \(1\,\text{TB}=2^{40}\,\text{B}\) |
2、编址单位🧀
| 编址方式 | 一个地址对应的数据量 |
|---|---|
| 按字节编址 | \(1\,\text{B}=8\,\text{bit}\) |
| 按半字编址 | 半个机器字,常见为 2 B |
| 按字编址 | 一个机器字,常见为 4 B 或 8 B,取决于体系结构 |
做容量题必须先确定一个地址对应多少字节。同样是 \(m\) 位地址,按字节编址与按字编址得到的总字节数可能不同。
3、二维译码🧀
若有 \(n\) 位地址,一维译码需要选择 \(2^n\) 条字线。把地址分为行地址和列地址后,可以分别使用两个译码器。
当 \(n\) 为偶数并平均划分时,译码器输出线总数由 \(2^n\) 减少为:
减少的输出线数量为:
当 \(n\) 为奇数时,行、列地址位数通常分别取 \(\lfloor n/2\rfloor\) 和 \(\lceil n/2\rceil\)。
4、边界对齐与端序🧀
数据按自然边界对齐时,一个 \(N\) 字节对象的起始地址通常是 \(N\) 的整数倍。对齐可能引入填充字节,但能减少跨存储字或跨总线传输的访问。
- 小端:低有效字节放在低地址。
- 大端:高有效字节放在低地址。
结构体对齐与小端存储
假设首地址为 0xC008,各类型按自然边界对齐,record.a = 273,机器采用小端方式。
因此 a 在内存中排列为:
| 地址 | 内容 |
|---|---|
C008 | 11 |
C009 | 01 |
C00A | 00 |
C00B | 00 |
b 位于 C00C。为了让 2 B 的 short 从偶数地址开始,C00D 是填充字节,c 从 C00E 开始。
Warning
不要机械地记“某种类型后固定空几个字节”。填充量由当前地址、下一成员的对齐要求以及具体 ABI 共同决定。
三、半导体存储器🧀
1、SRAM 与 DRAM🧀
| 对比 | SRAM | DRAM |
|---|---|---|
| 中文 | 静态随机存取存储器 | 动态随机存取存储器 |
| 存储单元 | 双稳态触发器,常见为 6T | 1 个晶体管和 1 个电容 |
| 是否刷新 | 不需要 | 需要周期刷新 |
| 读出是否破坏 | 非破坏性 | 通常为破坏性读出,读后要恢复 |
| 速度 | 快 | 较慢 |
| 集成度 | 低 | 高 |
| 单位容量成本 | 高 | 低 |
| 常见用途 | Cache | 主存 |
若芯片规格为 \(N\times M\) 位:
- 存储单元数为 \(N\),数据宽度为 \(M\) 位。
- 非复用地址需要 \(\lceil\log_2N\rceil\) 根地址线。
- 需要 \(M\) 根数据线。
- 完整引脚数还应按题意加入片选、读写控制、电源和地等引脚。
2、DRAM 地址复用与刷新🧀
DRAM 通常把地址分成行地址和列地址,复用同一组地址引脚:先由 RAS 锁存行地址,再由 CAS 锁存列地址。
若 \(N\) 个单元恰好排列为近似方阵,复用后的地址引脚数约为:
再加上 \(M\) 根数据线以及 RAS、CAS、WE 等控制线。芯片是否另有 CS、OE 等引脚必须以题目给出的接口为准。
DRAM 以行为单位刷新,只需要给出行地址:
| 刷新方式 | 做法 | 特点 |
|---|---|---|
| 集中刷新 | 在一个时间段连续刷新所有行 | 刷新期间形成较长“死区” |
| 分散刷新 | 每个存储周期都安排一次刷新 | 没有集中死区,但正常访问速度下降 |
| 异步刷新 | 在最大刷新间隔内均匀分配各行刷新 | 死区短,兼顾访问与刷新 |
- 最大刷新间隔:某一行在数据可能丢失前必须再次刷新的最长时间。
- 刷新周期:系统实际安排完成相邻刷新操作的时间间隔,或者题目定义的一轮刷新时间;不同教材表述略有差异,应结合题意。
在其他条件相同时,行数较少意味着一轮刷新需要的刷新命令较少,但阵列组织还受芯片面积、速度和接口设计制约,不能简单认为“行越少一定越好”。
3、SDRAM🧀
SDRAM 使用时钟与存储器总线同步工作,并支持突发传输。同步接口简化了控制时序,突发传输则适合连续访问一行中的多个数据。
4、ROM 与 Flash🧀
| 类型 | 特点 |
|---|---|
| Mask ROM | 出厂时写入,不能由用户修改 |
| PROM | 用户可编程一次 |
| EPROM | 可用紫外线擦除后重新编程 |
| EEPROM | 可电擦除,通常支持较细粒度改写 |
| Flash | 以块为单位擦除,密度高,广泛用于 SSD 和嵌入式存储 |
Flash 读取通常较快,但写入前可能需要擦除整个块,因此写入和擦除明显慢于读取,并且擦写次数有限。
四、存储芯片扩展🧀
设单片芯片规格为 \(N\times M\) 位,目标存储器规格为 \(N_t\times M_t\) 位。若二者能够整除,需要的芯片数为:
计算时可以先把所有容量统一换算为 bit,避免混淆 B 和 bit。
1、位扩展🧀
位扩展解决“每个存储字太窄”的问题:
- 各芯片地址线并联。
- 各芯片控制线并联。
- 多片芯片同时工作,每片负责数据总线中的不同位。
例如,把 \(256K\times8\) 扩展为 \(256K\times32\),需要 \(32/8=4\) 片并行工作。
2、字扩展🧀
字扩展解决“存储字数量不足”的问题:
- 低位地址连接到所有芯片,用于片内寻址。
- 高位地址经过译码器产生片选信号。
- 同一时刻通常只选中一个芯片组。
- 各组共用数据总线。
例如,把 \(256K\times8\) 扩展为 \(2M\times8\),需要 \(2M/256K=8\) 片。
3、字位同时扩展🧀
当存储字数量和数据宽度都不足时,先按位组成一组,再用多个组进行字扩展。
计算 ROM 和 RAM 芯片数
某主存容量为 \(64\,\text{KB}\),其中 ROM 区为 \(4\,\text{KB}\),其余为 RAM,按字节编址。使用 \(2K\times8\) 位 ROM 和 \(4K\times4\) 位 RAM。
ROM 单片容量为:
因此 ROM 需要:
RAM 总容量为 \(60\,\text{KB}\)。两片 \(4K\times4\) 先做位扩展,得到一组 \(4K\times8=4\,\text{KB}\);再用 15 组进行字扩展:
判断芯片地址范围
用 \(2K\times4\) 位芯片组成 \(8K\times8\) 位存储器。每两个芯片先组成一个 \(2K\times8\) 位组,再用四组扩展字数。
每组覆盖 \(2K=0x800\) 个地址,因此各组起始地址为 0000H、0800H、1000H、1800H。
地址 0B1FH 位于 0800H~0FFFH,所以同时被选中的两片芯片的最小地址均为:
五、Cache🧀
1、基本原理🧀
Cache 位于 CPU 与主存之间,通常由 SRAM 构成。主存和 Cache 都被划分为大小相同的块,数据在二者之间以块为单位调入和换出。
- 时间局部性:刚访问过的数据很可能再次访问。
- 空间局部性:某地址附近的数据很可能很快被访问。
访问地址时,Cache 用地址中的 Tag 判断目标主存块是否已经保存在候选行中;命中则直接返回数据,未命中则从下一级存储器调入整个块。
2、性能指标🧀
设 Cache 命中时间为 \(t_c\),未命中后访问主存并调入数据的额外时间为 \(t_m\),命中率为 \(h\):
缺失率为 \(1-h\)。若题目把 \(t_m\) 定义为“未命中时的完整访问时间”而不是额外罚时,则应按题目定义重新列式,不能直接套用上式。
若题目把 Cache 的命中时间作为理想访问时间,可用下式表示简化访问效率:
3、地址划分🧀
设主存地址共 \(m\) 位,按字节编址,每块为 \(2^b\) B,因此块内偏移占 \(b\) 位。
(1)直接映射🧀
主存中的每个块只能放入唯一的一行:
若 Cache 有 \(2^r\) 行:
每次只比较一个候选行的标记,硬件简单,但多个主存块映射到同一行时容易产生冲突缺失。
(2)全相联映射🧀
主存中的任意块可以放入 Cache 的任意一行:
需要同时比较所有 Cache 行的标记,冲突少但硬件成本高。
(3)组相联映射🧀
Cache 先分为若干组,主存块只能进入唯一的一组,但可以放入组内任意一路:
若 Cache 共 \(2^r\) 行、每组有 \(k\) 路,则组数为 \(2^r/k=2^s\):
每组需要 \(k\) 个标记比较器,比较器宽度为 Tag 位数。直接映射可视为 1 路组相联,全相联可视为只有 1 组。
4、直接映射例题🧀
已知:
- 主存地址 32 位,按字节编址。
- Cache 数据区为 \(32\,\text{KB}\)。
- 块大小为 \(32\,\text{B}\)。
- 采用直接映射和写回策略。
Cache 行数为:
所以 Index 为 10 位,Offset 为 5 位,Tag 为:
每行数据区为:
加上 17 位 Tag、1 位有效位和写回策略所需的 1 位脏位,每行至少需要:
Tip
Cache 标称容量通常只计算数据区,不包含 Tag、有效位、脏位和替换状态位。题目若问“总容量”或“实际位数”,才需要把这些额外位加入。
若机器字长为 32 bit,则一个字是 4 B;主存块包含 4 个字时,块大小是 \(4\times4=16\) B,而不是 4 B。
5、替换算法🧀
直接映射只有唯一候选行,不需要替换算法;组相联和全相联在候选位置已满时需要选择牺牲块。
| 算法 | 选择方式 | 注意点 |
|---|---|---|
| RAND | 随机选择 | 硬件简单,结果不稳定 |
| FIFO | 替换最早进入的块 | 不考虑近期是否频繁访问 |
| LRU | 替换最长时间未被访问的块 | 符合时间局部性,路数高时实现成本大 |
| LFU | 替换访问次数最少的块 | 旧数据可能因历史计数过大长期不被换出,常配合衰减机制 |
对于 \(k\) 路组相联,单个年龄计数器可能需要 \(\lceil\log_2k\rceil\) 位,但精确 LRU 的全部状态不能简单概括为整个组只需 \(\log_2k\) 位。2 路组相联只需 1 位指出最近使用的一路;更高路数常采用近似 LRU。
6、写策略🧀
写命中时:
| 策略 | 做法 | 特点 |
|---|---|---|
| 写回 Write Back | 只修改 Cache,块被替换时才写回主存 | 写主存次数少,需要脏位 |
| 写穿 Write Through | 同时修改 Cache 和主存 | 一致性简单,写流量大,常配合写缓冲 |
写不命中时:
| 策略 | 做法 |
|---|---|
| 写分配 Write Allocate | 先把目标块调入 Cache,再在 Cache 中写入 |
| 非写分配 No Write Allocate | 不调入 Cache,直接写下一级存储器 |
常见组合是“写回 + 写分配”和“写穿 + 非写分配”,但这不是强制规则,仍应以具体系统或题目为准。