一、存储系统的层次结构
现代计算机通过多级存储体系来平衡速度、容量与成本之间的矛盾。该层次结构从 CPU 内部的寄存器开始,逐级向下扩展至脱机存储设备,形成如下层级:
- CPU 内部通用寄存器:位于处理器内部,访问速度最快,容量极小(通常几十到几百个寄存器),用于暂存指令执行过程中的操作数和地址。
- Cache(高速缓存):分为 L1、L2、L3 多级缓存,由 SRAM 构成,速度接近 CPU,用于缓存主存中频繁访问的数据和指令。Cache 与主存之间的调度完全由硬件自动管理,对程序员透明。
- 主存储器(主存):主要由 DRAM 构成,存放当前运行的程序和数据,可被 CPU 直接访问,容量较大但断电后数据丢失。
- 联机磁盘存储器:如固态硬盘(SSD)、机械硬盘(HDD),用于长期保存正在使用或近期可能使用的大量数据,需通过 I/O 系统调入主存才能被 CPU 访问。
- 脱机光盘 / 磁带存储器:如 DVD、蓝光光盘、磁带库等,用于归档和备份,需人工介入加载,访问延迟高,但单位成本低、容量极大。
这一结构实现了:
- 速度由快到慢:寄存器 > Cache > 主存 > 磁盘 > 脱机存储
- 容量由小到大:寄存器 < Cache < 主存 < 磁盘 < 脱机存储
- 单位成本由高到低:寄存器最贵,脱机存储最便宜
其中,Cache 与主存之间的数据交换由硬件(如 Cache 控制器)自动完成;而主存与辅存之间的数据调入调出则由操作系统(软件)与 MMU(内存管理单元,硬件)协同实现,典型技术包括虚拟内存和页面置换。
二、存储器的分类及解读
-
按位置分类
- 内存(主存):安装在主板上,直接与 CPU 通信,用于运行时程序和数据的实时支持。特点是速度快、容量有限、价格较高。
- 外存(辅存):独立于主机核心系统之外,不能被 CPU 直接寻址,必须经由 I/O 接口将数据加载进内存后方可使用。优点是容量大、持久化存储、成本低。
-
按构成材料分类
类别 工作原理 典型设备 磁存储器 利用磁性材料的磁化方向存储信息 磁芯存储器(早期)、磁带、硬盘 半导体存储器 基于晶体管电路存储电荷或状态 SRAM(静态 RAM)、DRAM(动态 RAM) 光存储器 使用激光读写凹坑反射差异表示数据 CD-ROM、DVD、Blu-ray 光盘 -
按工作方式分类
-
随机读写存储器(RAM)
- 支持任意地址的快速读写操作。
- 分为两类:
- SRAM:基于触发器结构,无需刷新,速度快,用于 Cache。
- DRAM:基于电容充放电,需定期刷新,速度较慢,用于主存。
- 断电后内容丢失,属于易失性存储器。
-
只读存储器(ROM)
- 正常工作时只能读取,断电后数据不丢失,属于非易失性存储器。
- 细分类型:
- Mask ROM:制造时固化数据,不可更改,适用于量产固定程序。
- PROM(Programmable ROM):用户可用专用编程器一次性写入,之后不可修改,适合小批量定制。
- EPROM(Erasable PROM):可通过紫外线擦除后重新编程,封装有石英窗口。
- EEPROM(Electrically Erasable PROM):可用电信号逐字节擦写,寿命有限(约 10 万次),用于 BIOS 设置存储。
- Flash Memory:一种改进型 EEPROM,支持块擦除和快速写入,广泛用于 U 盘、SSD、嵌入式系统中。
Cache 与主存之间的数据交互依赖于地址映射机制和替换策略,其核心目标是:在有限的 Cache 容量下,尽可能高效地缓存主存中被频繁访问的数据,从而提升 CPU 访问速度。
-
一、Cache 与主存的地址映射方式
由于 Cache 容量远小于主存,不能容纳所有主存数据,因此需要通过某种规则将主存块“映射”到 Cache 中。主要有三种映射方式:
1. 直接映射(Direct Mapping)
- 每个主存块只能映射到 唯一确定的 Cache 行(也称槽位)。
- 映射公式:
Cache 行号=(主存块号)mod (Cache 总行数) \text{Cache 行号} = (\text{主存块号}) \mod (\text{Cache 总行数}) Cache 行号=(主存块号)mod(Cache 总行数) - 特点:
- 结构简单、成本低、查找速度快。
- 缺点是容易发生冲突:不同主存块竞争同一个 Cache 行,导致频繁替换,降低命中率。
- 使用场景:对性能要求不高或嵌入式系统中。
示例:若 Cache 有 64 行,则主存中所有块号模 64 相同的块都映射到同一行。
2. 全相联映射(Fully Associative Mapping)
- 任意主存块可以装入 Cache 的任意空闲行。
- 查找时需同时比对所有 Cache 行的标签(Tag),采用“并行比较”硬件实现。
- 特点:
- 灵活性最高,命中率高。
- 实现复杂、成本高(需要多个比较器)、功耗大。
- 使用场景:小容量 Cache(如 L1 指令 Cache)中使用较多。
3. 组相联映射(Set-Associative Mapping) —— 最常用
- 将 Cache 分为若干“组”,每组包含多个行(例如 2 路、4 路、8 路等)。
- 主存块先按模运算映射到某一个组,然后可在该组内的任意空闲行中存放。
- 例如:4 路组相联 = 每组 4 行,共 N 组 → 总行数 = 4×N
- 映射公式:
组号=(主存块号)mod (总组数) \text{组号} = (\text{主存块号}) \mod (\text{总组数}) 组号=(主存块号)mod(总组数) - 在组内进行并行标签匹配,灵活性与效率平衡良好。
✅ 现代处理器普遍采用 4 路、8 路甚至 16 路组相联映射,兼顾命中率与硬件开销。
二、替换策略(Replacement Policy)
当 Cache 已满且需要加载新块时,必须选择一个旧块替换出去。常用的替换算法有:
1. 最近最少使用(LRU, Least Recently Used)
- 替换掉最长时间未被访问的块。
- 实现方式:为每个 Cache 行维护访问时间戳或链表排序。
- 优点:符合程序局部性原理,命中率较高。
- 缺点:硬件开销较大,尤其在多路组相联中。
- 应用:常见于 2~8 路组相联 Cache。
2. 先进先出(FIFO, First In First Out)
- 按进入顺序排队,最早进入的先被替换。
- 实现简单(可用环形队列)。
- 缺点:不考虑使用频率,可能误删常用块。
3. 随机替换(Random)
- 随机选择一个块替换。
- 硬件实现最简单,无需记录状态。
- 在某些处理器(如 MIPS、ARM)中用于简化设计,实际表现有时接近 LRU。
✅ 实际系统中常采用 近似 LRU 算法(如计数器法、堆栈法)以降低硬件复杂度。
三、Cache 命中与缺失处理流程
- CPU 发出内存访问请求(地址)。
- 地址被划分为三部分:
- Tag(标签):标识来自哪个主存区域。
- Index(索引):定位到 Cache 的某一行或组。
- Offset(偏移):定位块内具体字节。
- 根据 Index 找到对应 Cache 行(或组),并与 Tag 进行比对:
- 若匹配且有效位为 1 → 命中,直接返回数据。
- 否则 → 缺失(Miss),触发以下操作:
- 发起主存访问,读取整个数据块。
- 在 Cache 中选择一行(根据映射规则和替换策略)替换。
- 加载新块,更新 Tag 和有效位。
- 返回数据给 CPU。
四、写操作策略
Cache 写入还需考虑如何同步主存:
1. 写直达(Write-through)
- 每次写 Cache 同时写主存。
- 优点:一致性好,简单可靠。
- 缺点:频繁写主存影响性能,增加总线负载。
2. 写回(Write-back)
- 只写 Cache,标记该行为“脏”(Dirty Bit)。
- 当该块被替换时才写回主存。
- 优点:减少主存写次数,提高效率。
- 缺点:需维护脏位,恢复时需额外判断。
✅ 现代系统多采用 写回 + LRU 替换 + 组相联映射 的组合方案。
# 简化示例:模拟一个 4 路组相联 Cache 的查找过程(伪代码)
class CacheSet:
def __init__(self):
self.lines = [{'valid': False, 'tag': None, 'data': None, 'lru_count': 0} for _ in range(4)]
def find(self, tag):
for i, line in enumerate(self.lines):
if line['valid'] and line['tag'] == tag:
return i # 返回命中位置
return -1 # 未命中
def replace(self, new_tag, new_data):
# 找 LRU 最小的行(最久未使用)
victim_idx = min(range(4), key=lambda x: self.lines[x]['lru_count'])
self.lines[victim_idx] = {
'valid': True,
'tag': new_tag,
'data': new_data,
'lru_count': max(line['lru_count'] for line in self.lines) + 1
}
return victim_idx


5231

被折叠的 条评论
为什么被折叠?



