# 第4章 存储器
> 📖 本章介绍存储器的分类、工作原理和层次结构,是课程的重点和难点。
> 🎯 重点理解存储层次、主存组成、Cache和虚拟存储器
---
## 📋 本章目录
- [[#4.1 概述]]
- [[#4.2 主存储器]]
- [[#4.3 高速缓冲存储器]]
- [[#4.4 虚拟存储器]]
- [[#本章小结]]
---
## 4.1 概述
### 4.1.1 存储器分类
```mermaid
graph TB
A[存储器分类] --> B[按存储介质]
A --> C[按存取方式]
A --> D[按作用]
B --> B1[半导体存储器]
B --> B2[磁表面存储器]
B --> B3[光盘存储器]
C --> C1[随机存储器RAM]
C --> C2[只读存储器ROM]
C --> C3[串行访问存储器]
D --> D1[主存储器]
D --> D2[辅助存储器]
D --> D3[缓冲存储器Cache]
```
#### 按存储介质分类
| 类型 | 特点 | 示例 |
|------|------|------|
| 半导体存储器 | 体积小、功耗低、速度快、易失性 | SRAM、DRAM |
| 磁表面存储器 | 非易失、容量大、速度慢 | 磁盘、磁带 |
| 光盘存储器 | 非易失、容量大、可移动 | CD、DVD |
#### 按存取方式分类
| 类型 | 特点 | 应用 |
|------|------|------|
| **RAM** | 随机读写、存取时间与位置无关 | 主存 |
| **ROM** | 只读不写(或受限写入) | 固件、BIOS |
| **顺序存取** | 按物理顺序寻找(如磁带) | 磁带 |
| **直接存取** | 先直接定位区域,再顺序寻找(如磁盘) | 磁盘 |
#### RAM和ROM的细分
```mermaid
graph LR
RAM[随机存储器RAM] --> SRAM[静态RAM
触发器原理]
RAM --> DRAM[动态RAM
电容充放电]
ROM[只读存储器ROM] --> MROM[掩模ROM]
ROM --> PROM[可编程ROM]
ROM --> EPROM[可擦除PROM]
ROM --> EEPROM[电擦除PROM]
ROM --> Flash[闪速存储器]
```
### 4.1.2 存储器的层次结构
> [!important] 核心问题
> 速度、容量、价格三者不可兼得,需要通过层次结构解决矛盾。
#### 存储器性能指标关系
```mermaid
graph TB
subgraph "存储器层次结构"
REG["寄存器
速度最快、容量最小、价格最高"] --> CACHE["Cache
高速缓冲存储器"]
CACHE --> MEM["主存储器
主存"]
MEM --> DISK["磁盘
辅助存储器"]
DISK --> TAPE["磁带
辅助存储器"]
end
style REG fill:#ff9999
style CACHE fill:#ffcc99
style MEM fill:#ffff99
style DISK fill:#99ff99
style TAPE fill:#9999ff
```
| 层次 | 速度 | 容量 | 价格 | 位置 |
|------|------|------|------|------|
| 寄存器 | 最快 | 最小 | 最高 | CPU内部 |
| Cache | 很快 | 小 | 高 | CPU内部/外部 |
| 主存 | 较快 | 中等 | 中等 | 主板 |
| 磁盘 | 慢 | 大 | 低 | 外部设备 |
| 磁带 | 最慢 | 最大 | 最低 | 外部设备 |
#### 两个存储层次
```mermaid
graph TB
subgraph "缓存-主存层次"
CPU1[CPU] <--> |"速度接近Cache"| CACHE[Cache]
CACHE <--> |"容量接近主存"| MEM1[主存]
end
subgraph "主存-辅存层次"
MEM2[主存] <--> |"速度接近主存"| DISK[辅存]
CPU2[CPU] -.-> |"不直接访问"| DISK
end
style CPU1 fill:#ff9999
style CPU2 fill:#ff9999
style CACHE fill:#ffcc99
style MEM1 fill:#ffff99
style MEM2 fill:#ffff99
style DISK fill:#99ff99
```
**缓存-主存层次**:
- 解决CPU和主存**速度不匹配**问题
- 数据调动由**硬件**自动完成
- 对程序员**透明**
**主存-辅存层次**:
- 解决存储系统**容量**问题
- 数据调动由**硬件+操作系统**完成
- 对程序员**透明**
---
## 4.2 主存储器
### 4.2.1 主存的基本组成
```mermaid
graph TB
subgraph "主存结构"
MAR[MAR
地址寄存器] --> |"地址"| DEC[译码器]
DEC --> |"选择信号"| MEM[存储体]
MEM <--> |"数据"| MDR[MDR
数据寄存器]
MDR <--> |"数据"| DBUS[数据总线]
MAR <--> |"地址"| ABUS[地址总线]
CTRL[控制电路] --> |"读写控制"| MEM
end
style MAR fill:#ff9999
style MDR fill:#99ccff
style MEM fill:#99ff99
style DEC fill:#ffcc99
```
**工作过程**:
1. **读操作**:CPU将地址送MAR → 地址总线 → 译码器选中单元 → 读出数据到MDR → 数据总线
2. **写操作**:CPU将地址送MAR,数据送MDR → 发写命令 → 数据写入选中单元
### 4.2.2 主存中存储单元地址的分配
#### 字节寻址与字寻址
```mermaid
graph TB
subgraph "IBM 370(32位字长)"
A1["地址0"] --> B1["字节0"]
A2["地址1"] --> B2["字节1"]
A3["地址2"] --> B3["字节2"]
A4["地址3"] --> B4["字节3"]
A5["地址4(字地址)"] --> B5["下一个字"]
end
subgraph "PDP-11(16位字长)"
C1["地址0(字地址)"] --> D1["字节0"]
C2["地址1"] --> D2["字节1"]
C3["地址2(字地址)"] --> D3["下一个字"]
end
```
> [!note] 地址表示
> - IBM 370:字地址 = 高位字节地址(4的倍数)
> - PDP-11:字地址 = 低位字节地址(2的倍数)
### 4.2.3 主存的技术指标
#### 1. 存储容量
$$存储容量 = 存储单元个数 \times 存储字长$$
**字节表示**:
$$存储容量 = 存储单元个数 \times 存储字长 / 8$$
> [!example] 示例
> 24位地址线,按字节寻址:
> - 寻址范围:$2^{24} = 16M$ 个字节
> - 若字长32位:按字寻址范围 = $16M / 4 = 4M$ 个字
#### 2. 存储速度
| 指标 | 定义 | 关系 |
|------|------|------|
| **存取时间** | 启动一次操作到完成的时间 | 读出时间或写入时间 |
| **存取周期** | 连续两次操作的最小间隔 | 存取周期 > 存取时间 |
| **存储器带宽** | 单位时间存取信息量 | 带宽 = 存储字长 / 存取周期 |
**提高带宽的措施**:
1. 缩短存取周期
2. 增加存储字长
3. 增加存储体
### 4.2.4 半导体存储芯片
#### 芯片基本结构
```mermaid
graph LR
subgraph "存储芯片"
ADDR[地址线] --> DEC[译码驱动]
DEC --> MEM[存储矩阵]
MEM <--> RW[读/写电路]
RW <--> DATA[数据线]
CS[片选线] --> DEC
RW_CTRL[读写控制线] --> RW
end
```
**容量计算**:
$$芯片容量 = 2^{地址线位数} \times 数据线位数$$
> [!example] 示例
> - 10根地址线,4根数据线:$2^{10} \times 4 = 4K$ 位
> - 14根地址线,1根数据线:$2^{14} \times 1 = 16K$ 位
#### 译码驱动方式
| 方式 | 特点 | 适用场景 |
|------|------|----------|
| **线选法** | 一根字线直接选中一个单元 | 容量小的芯片 |
| **重合法** | X、Y两个方向译码,交叉点选中 | 大容量芯片 |
```mermaid
graph TB
subgraph "线选法(16×1字节)"
A1["A3A2A1A0 = 1111"] --> B1["第15根字线选中"]
B1 --> C1["8位数据读出"]
end
subgraph "重合法(1K×1位)"
A2["地址译码"] --> B2["X方向32根线"]
A2 --> C2["Y方向32根线"]
B2 --> D2["交叉点选中1位"]
C2 --> D2
end
```
### 4.2.5 随机存取存储器(RAM)
#### 静态RAM(SRAM)
**基本单元电路**:6个MOS管组成的触发器
**特点**:
- 用触发器原理寄存信息
- 速度快,不需要刷新
- 集成度低,功耗大
- 用于Cache
**典型芯片**:Intel 2114(1K×4位)
#### 动态RAM(DRAM)
**基本单元电路**:1个MOS管 + 电容
**特点**:
- 用电容充放电原理寄存信息
- 需要定期**刷新**
- 集成度高,功耗小
- 用于主存
**典型芯片**:Intel 4116(16K×1位)
**刷新方式**:
| 方式 | 特点 | 效率 |
|------|------|------|
| 集中刷新 | 一段时间集中刷新所有行 | 有死区 |
| 分散刷新 | 每行刷新分散到各周期 | 无死区,效率低 |
| 异步刷新 | 各行刷新均匀分散 | 折中方案 |
#### SRAM vs DRAM 对比
| 特性 | SRAM | DRAM |
|------|------|------|
| 存储原理 | 触发器 | 电容 |
| 速度 | 快 | 较慢 |
| 集成度 | 低 | 高 |
| 功耗 | 大 | 小 |
| 刷新 | 不需要 | 需要 |
| 价格 | 高 | 低 |
| 应用 | Cache | 主存 |
### 4.2.6 只读存储器(ROM)
| 类型 | 特点 | 应用 |
|------|------|------|
| **MROM** | 厂家掩模制作,不可更改 | 批量生产 |
| **PROM** | 用户可编程一次 | 小批量 |
| **EPROM** | 紫外线擦除,可重复编程 | 开发调试 |
| **EEPROM** | 电擦除,可重复编程 | 参数存储 |
| **Flash** | 电擦除,速度快 | U盘、SSD |
### 4.2.7 存储器与CPU的连接
#### 存储器容量扩展
**三种扩展方式**:
```mermaid
graph TB
subgraph "位扩展"
A1["芯片1(8位)"] --> C1["组合(16位)"]
A2["芯片2(8位)"] --> C1
end
subgraph "字扩展"
B1["芯片1(1K×8)"] --> D1["组合(2K×8)"]
B2["芯片2(1K×8)"] --> D1
end
subgraph "字位同时扩展"
E1["芯片1"] --> F1["组合"]
E2["芯片2"] --> F1
E3["芯片3"] --> F1
E4["芯片4"] --> F1
end
```
| 扩展方式 | 连接方法 | 应用场景 |
|----------|----------|----------|
| **位扩展** | 地址线、控制线并联,数据线串联 | 增加字长 |
| **字扩展** | 地址线、数据线、控制线并联,片选线用高位地址 | 增加字数 |
| **字位扩展** | 先位扩展,再字扩展 | 大容量存储器 |
#### 存储器与CPU连接示例
```mermaid
graph TB
subgraph "CPU"
MAR[MAR] --> |"地址"| ADDR[地址总线]
MDR[MDR] <--> |"数据"| DATA[数据总线]
CTRL[控制信号] --> |"读写"| RW[读写控制]
end
subgraph "存储器"
ADDR --> |"地址"| CHIP1[芯片1]
ADDR --> |"地址"| CHIP2[芯片2]
CHIP1 <--> |"数据"| DATA
CHIP2 <--> |"数据"| DATA
CS1[片选1] --> CHIP1
CS2[片选2] --> CHIP2
RW --> CHIP1
RW --> CHIP2
end
```
### 4.2.8 存储器的校验
#### 奇偶校验
**原理**:增加1位校验位,使数据中1的个数为奇数(奇校验)或偶数(偶校验)。
**特点**:
- 简单,只能检测奇数位错误
- 不能纠错
#### 海明码(Hamming Code)
**原理**:在数据位中插入多个校验位,通过校验位的组合定位错误位。
**校验位数r满足**:$2^r \geq m + r + 1$(m为数据位数)
**特点**:
- 可以检测并纠正1位错误
- 可以检测2位错误
> [!example] 示例
> 8位数据需要4位校验码:
> - 总位数:8 + 4 = 12位
> - 校验位位置:1, 2, 4, 8
> - 可纠正1位错误
---
## 4.3 高速缓冲存储器(Cache)
### 4.3.1 Cache的基本原理
> [!important] 为什么需要Cache
> CPU速度远快于主存,Cache作为中间缓冲,解决速度不匹配问题。
```mermaid
graph LR
CPU[CPU] <--> |"速度匹配"| CACHE[Cache]
CACHE <--> |"容量大"| MEM[主存]
style CPU fill:#ff9999
style CACHE fill:#ffcc99
style MEM fill:#99ff99
```
**工作原理**:
1. CPU访问数据时,先查Cache
2. **命中**:直接从Cache读取(快)
3. **未命中**:从主存读取,同时调入Cache
### 4.3.2 Cache命中率
**命中率**:
$$H = \frac{N_c}{N_c + N_m}$$
- $N_c$:Cache访问次数
- $N_m$:主存访问次数
**平均访问时间**:
$$T_a = HT_c + (1-H)T_m$$
**访问效率**:
$$e = \frac{T_c}{T_a} = \frac{1}{H + (1-H)r}$$
其中 $r = T_m / T_c$(主存与Cache访问时间之比)
> [!example] 示例
> Cache命中率95%,Cache访问时间10ns,主存访问时间100ns:
> $$T_a = 0.95 \times 10 + 0.05 \times 100 = 14.5ns$$
> $$e = \frac{10}{14.5} = 69\%$$
### 4.3.3 Cache地址映射
> [!info] 问题
> 主存块如何放入Cache?需要地址映射。
#### 三种映射方式
##### 1. 直接映射(Direct Mapping)
**规则**:每个主存块只能映射到Cache的固定行。
**映射公式**:$i = j \mod m$
- i:Cache行号
- j:主存块号
- m:Cache行数
```mermaid
graph LR
subgraph "直接映射"
M0[主存块0] --> C0[Cache行0]
M1[主存块1] --> C1[Cache行1]
Mm[主存块m] --> Cm[Cache行m]
Mm1[主存块m+1] --> C0
end
```
**地址结构**:
| 标记(Tag) | Cache行号(Index) | 块内地址(Offset) |
|-------------|-------------------|-------------------|
**优点**:实现简单,查找速度快
**缺点**:灵活性差,命中率低
##### 2. 全相联映射(Fully Associative Mapping)
**规则**:主存块可以映射到Cache的任意行。
```mermaid
graph LR
subgraph "全相联映射"
M0[主存块0] --> |"任意行"| CACHE[Cache]
M1[主存块1] --> |"任意行"| CACHE
Mn[主存块n] --> |"任意行"| CACHE
end
```
**地址结构**:
| 标记(Tag) | 块内地址(Offset) |
|-------------|-------------------|
**优点**:灵活性好,命中率高
**缺点**:查找速度慢,硬件成本高
##### 3. 组相联映射(Set Associative Mapping)
**规则**:将Cache分组,主存块映射到固定组,组内任意行。
**映射公式**:$q = j \mod u$
- q:Cache组号
- j:主存块号
- u:Cache组数
```mermaid
graph LR
subgraph "组相联映射"
M0[主存块0] --> G0[Cache组0]
M1[主存块1] --> G1[Cache组1]
Mu[主存块u] --> Gu[Cache组u]
Mu1[主存块u+1] --> G0
end
```
**地址结构**:
| 标记(Tag) | 组号(Index) | 块内地址(Offset) |
|-------------|--------------|-------------------|
**优点**:折中方案,兼顾速度和命中率
**应用**:现代Cache常用2路、4路、8路组相联
#### 三种映射方式对比
| 方式 | 映射规则 | 灵活性 | 命中率 | 硬件复杂度 |
|------|----------|--------|--------|------------|
| 直接映射 | 固定行 | 差 | 低 | 简单 |
| 全相联映射 | 任意行 | 好 | 高 | 复杂 |
| 组相联映射 | 固定组,任意行 | 中 | 中 | 中等 |
### 4.3.4 Cache替换算法
> [!info] 问题
> Cache满时,新块调入需要替换旧块,如何选择?
#### 常用替换算法
| 算法 | 原则 | 优点 | 缺点 |
|------|------|------|------|
| **FIFO** | 先进先出 | 实现简单 | 不考虑访问频率 |
| **LRU** | 最近最少使用 | 命中率高 | 实现复杂 |
| **LFU** | 最不经常使用 | 考虑频率 | 需要计数器 |
| **随机替换** | 随机选择 | 实现简单 | 命中率不稳定 |
**LRU算法实现**:
- 为每行设置计数器
- 每次访问,命中行计数器清零,其他行加1
- 替换计数器值最大的行
### 4.3.5 Cache写策略
> [!info] 问题
> Cache数据修改后,如何与主存保持一致?
#### 写策略
| 策略 | 方法 | 优点 | 缺点 |
|------|------|------|------|
| **写直达** | 同时写Cache和主存 | 数据一致性好 | 速度慢 |
| **写回法** | 只写Cache,替换时写回主存 | 速度快 | 数据可能不一致 |
```mermaid
graph TB
subgraph "写直达"
CPU1[CPU] --> |"写"| CACHE1[Cache]
CPU1 --> |"同时写"| MEM1[主存]
end
subgraph "写回法"
CPU2[CPU] --> |"写"| CACHE2[Cache]
CACHE2 --> |"替换时写回"| MEM2[主存]
end
```
### 4.3.6 Cache性能分析
**加速比**:
$$加速比 = \frac{T_m}{T_a} = \frac{r}{1 + (r-1)H}$$
**Cache容量选择**:
- 太小:命中率低
- 太大:成本高,速度可能下降
- 通常:几十KB到几MB
---
## 4.4 虚拟存储器
### 4.4.1 虚拟存储器的概念
> [!info] 定义
> 虚拟存储器是一个逻辑模型,程序员看到的地址空间远大于实际主存。
**特点**:
- **虚拟地址**(逻辑地址):程序员使用的地址
- **物理地址**(实地址):主存实际地址
- 地址转换由硬件和OS自动完成
```mermaid
graph TB
subgraph "虚拟存储器"
VA[虚拟地址空间
很大] --> |"地址转换"| PA[物理地址空间
较小]
PA --> MEM[主存]
MEM --> DISK[辅存]
end
```
### 4.4.2 页式虚拟存储器
**基本思想**:
- 虚拟地址空间划分为固定大小的**页**
- 主存划分为相同大小的**页框**
- 通过**页表**建立映射关系
**地址结构**:
| 虚页号 | 页内地址 |
|--------|----------|
**页表项**:
| 虚页号 | 实页号 | 有效位 | 修改位 |
|--------|--------|--------|--------|
**地址转换过程**:
1. 从虚拟地址取出虚页号
2. 查页表得到实页号
3. 实页号 + 页内地址 = 物理地址
### 4.4.3 TLB(快表)
> [!info] 问题
> 页表在主存中,每次访问都要查页表,速度慢。
**解决方案**:在CPU中设置TLB(Translation Lookaside Buffer)
**特点**:
- 高速小容量存储器
- 存放最近使用的页表项
- 命中率通常 > 99%
**访问流程**:
1. 先查TLB
2. **TLB命中**:直接得到物理地址(快)
3. **TLB未命中**:查页表,更新TLB
---
## 📝 本章小结
### 核心概念
1. **存储层次**:寄存器 → Cache → 主存 → 辅存
2. **主存组成**:存储体 + MAR + MDR + 译码驱动 + 读写电路
3. **RAM分类**:SRAM(快,用于Cache)、DRAM(慢,用于主存)
4. **Cache映射**:直接映射、全相联映射、组相联映射
5. **虚拟存储器**:虚拟地址 → 物理地址的转换
### 关键公式
- **存储容量** = 存储单元个数 × 存储字长
- **Cache命中率** $H = N_c / (N_c + N_m)$
- **平均访问时间** $T_a = HT_c + (1-H)T_m$
- **访问效率** $e = T_c / T_a$
### 重点图示
> [!summary] 必须掌握的图
> 1. 存储器层次结构图
> 2. 主存基本组成框图
> 3. Cache三种映射方式示意图
> 4. Cache访问流程图
---
## 🧪 自测练习
### 概念题
1. 说明存储器层次结构的必要性。
2. 比较SRAM和DRAM的特点。
3. 解释Cache三种映射方式的区别。
### 计算题
1. 某系统Cache命中率90%,Cache访问时间20ns,主存访问时间200ns,求平均访问时间和访问效率。
2. 24位地址线,按字节寻址,存储字长32位,求存储容量。
### 分析题
1. 设计一个16K×8位的存储器,使用4K×4位的芯片。
2. 说明Cache写策略的选择依据。
---
## 🔗 相关链接
- [[03_系统总线]] - 上一章
- [[05_输入输出系统]] - 下一章
- [[08_CPU结构与功能]] - CPU访问存储器
- [[06_运算方法]] - 数据表示
---
*本章难度:⭐⭐⭐⭐⭐ 困难*
*重要程度:⭐⭐⭐⭐⭐ 核心重点*