> ## Documentation Index
> Fetch the complete documentation index at: https://docs.0907world.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 第 4 章 存储系统

> 整理主存、磁盘、Cache 与虚拟存储器的组织结构、地址映射、性能计算和常见易错点。

本章按“存储层次 → 主存储器 → 外部存储器 → Cache → 虚拟存储器”整理存储系统。复习时先建立层次结构和地址映射的整体框架，再重点掌握容量、地址位数、存取时间和命中率等计算。

<Tip>
  计算题的核心线索始终是“容量决定地址位数，地址划分决定映射关系”。遇到 Cache 或虚拟存储器题目时，先写出地址字段，再代入公式。
</Tip>

## 存储器概述

### 存储层次与分类

<AccordionGroup>
  <Accordion title="存储层次结构">
    从 CPU 到外部存储器，典型层次为：

    | 层次      | 典型介质         | 主要特点                |
    | ------- | ------------ | ------------------- |
    | CPU 寄存器 | 触发器          | 速度最快、容量最小、位于 CPU 内部 |
    | Cache   | SRAM         | 接近 CPU 速度，存放近期活跃数据  |
    | 主存储器    | DRAM         | 可直接被 CPU 访问，容量较大    |
    | 外部存储器   | 磁盘、SSD、磁带、光盘 | 非易失、容量大、速度慢         |

    越靠近 CPU 的层次速度越快、单位容量成本越高、容量越小。层次结构能够同时获得接近高速层的访问速度和接近低速层的容量与价格。
  </Accordion>

  <Accordion title="存储器的分类">
    | 分类依据 | 类型      | 示例或说明                   |
    | ---- | ------- | ----------------------- |
    | 存储元件 | 半导体存储器  | DRAM、SRAM               |
    | 存储元件 | 磁表面存储器  | 磁盘、磁带                   |
    | 存储元件 | 光存储器    | 光盘                      |
    | 存取方式 | 随机存取存储器 | RAM，访问时间与地址位置基本无关       |
    | 存取方式 | 顺序存取存储器 | 磁带，必须按顺序访问              |
    | 存取方式 | 直接存取存储器 | 机械磁盘，先定位磁道，再在磁道内顺序访问    |
    | 存取方式 | 相联存储器   | 按内容查找，例如 TLB 中采用的相联查找思想 |
    | 可改性  | 可读可写存储器 | RAM、部分 Flash            |
    | 可改性  | 只读存储器   | ROM、PROM、EPROM、EEPROM   |
    | 可保存性 | 易失性存储器  | RAM，断电后信息丢失             |
    | 可保存性 | 非易失性存储器 | ROM、Flash、磁盘、光盘等        |
  </Accordion>

  <Accordion title="命中率与平均访问时间">
    命中率表示访问落在高速层中的比例：

    $$
    H = \frac{N_{\text{hit}}}{N_{\text{hit}} + N_{\text{miss}}}
    $$

    若一次访问命中时耗时 $t_h$，未命中时总耗时 $t_m$，则平均访问时间为：

    $$
    T_{\text{avg}} = H \cdot t_h + (1-H) \cdot t_m
    $$

    <Info>
      $t_m$ 表示一次未命中访问的总时间，不一定是“缺失代价”。若题目给出的未命中时间仅为额外开销，应写成 $T_{\text{avg}} = t_h + (1-H) \cdot t_{\text{miss\_penalty}}$。
    </Info>
  </Accordion>
</AccordionGroup>

### 局部性原理

* **时间局部性**：刚被访问过的数据或指令，在后续访问中很可能再次被访问。循环变量、循环体代码是典型例子。
* **空间局部性**：刚被访问过的存储单元，其相邻单元在后续访问中很可能被访问。顺序访问数组、顺序执行指令是典型例子。

<Info>
  Cache 之所以有效，是因为程序同时具有时间局部性和空间局部性。若题目询问“为什么一次调入一个数据块”，应从空间局部性解释。
</Info>

## 主存储器

### 半导体随机存取存储器

<AccordionGroup>
  <Accordion title="SRAM 与 DRAM 的对比">
    | 对比项    | SRAM                   | DRAM                   |
    | ------ | ---------------------- | ---------------------- |
    | 存储元    | 双稳态触发器，典型结构为 6 个 MOS 管 | 电容，典型结构为 1 个晶体管和 1 个电容 |
    | 读出特性   | 非破坏性读出                 | 破坏性读出，访问后由灵敏放大器再生      |
    | 是否需要刷新 | 不需要                    | 需要定时刷新                 |
    | 集成度与成本 | 集成度低、单位容量成本高           | 集成度高、单位容量成本低           |
    | 速度与功耗  | 速度快，静态功耗相对较高           | 速度较慢，需要刷新              |
    | 常见用途   | Cache、TLB              | 主存储器                   |

    <Tip>
      SRAM 的“静态”指只要不断电，数据就能稳定保持，并不表示访问时不需要时钟或控制信号。
    </Tip>
  </Accordion>

  <Accordion title="DRAM 的刷新">
    DRAM 用电容保存数据，电荷会因漏电逐渐丢失，因此必须在规定时间内逐行读出并再生，这个过程称为刷新。

    **刷新的关键结论：**

    * 刷新以行为单位，不需要列地址，也不需要 CAS。
    * 刷新一遍所需的操作次数等于 DRAM 的行数。
    * RAS-only 刷新由外部提供行地址；自动刷新由片内刷新计数器产生行地址。
    * 刷新周期通常指全部存储单元必须完成一次刷新的最大时间，例如某器件规定为 $64\text{ ms}$。

    若共有 $R$ 行，刷新周期为 $T_{\text{refresh}}$，则平均刷新间隔为：

    $$
    t_{\text{refresh\_avg}} = \frac{T_{\text{refresh}}}{R}
    $$

    例如某 DRAM 有 2048 行，刷新周期为 $64\text{ ms}$，则在 $64\text{ ms}$ 内需要完成 2048 次行刷新，平均每隔

    $$
    \frac{64\text{ ms}}{2048} = 31.25\ \mu\text{s}
    $$

    刷新一行。

    **三种刷新安排：**

    | 方式   | 特点                        |
    | ---- | ------------------------- |
    | 集中刷新 | 在刷新周期末端集中刷新全部行，存在较长的“死时间” |
    | 分散刷新 | 每个存取周期后安排一次刷新，系统访问速度会被拖慢  |
    | 异步刷新 | 将刷新操作均匀分散，同时兼顾访存效率和刷新期限   |
  </Accordion>

  <Accordion title="存储芯片的组成与 DRAM 示例">
    DRAM 芯片通常由存储体、地址译码器、I/O 电路、片选控制逻辑和读写控制线组成。

    <img src="https://mintcdn.com/0907/gm0eG4U8fupDhHfo/images/computer-architecture/Chapter4_DRAM.png?fit=max&auto=format&n=gm0eG4U8fupDhHfo&q=85&s=07626848e7e810fa1fe0e27668c08a5a" alt="DRAM 芯片" width="982" height="548" data-path="images/computer-architecture/Chapter4_DRAM.png" />

    根据图中引脚和存储阵列可以得到以下结论：

    1. 芯片有 11 根地址线 $A_0 \sim A_{10}$ 和 4 根数据线 $D_0 \sim D_3$。在 RAS 和 CAS 控制下，同一组地址引脚分时传送行地址和列地址。
    2. 存储阵列为 $2048 \times 2048 \times 4$ 位，共有 4 个位平面。行地址和列地址各 11 位，同一行、列交叉处的 4 个位平面同时读写。
    3. 刷新时不需要列寻址。对于 RAS-only 刷新，外部提供行地址；对于自动刷新，片内刷新计数器产生行地址。
    4. 芯片共有 2048 行，因此将全部存储单元刷新一遍需要 2048 次行刷新。

    该芯片的总位数为：

    $$
    2048 \times 2048 \times 4 = 16\text{ Mbit} = 2\text{ MiB}
    $$

    <Tip>
      为减少地址引脚，行地址位数和列地址位数应尽量接近；在其他条件相同时，减少行数有利于降低刷新开销，但需要结合列地址位数和存储阵列结构综合考虑。
    </Tip>
  </Accordion>
</AccordionGroup>

### 存储器芯片的扩展

<AccordionGroup>
  <Accordion title="位扩展、字扩展与字位同时扩展">
    设目标存储器规格为“目标字数 × 目标字长”，单片芯片规格为“单片字数 × 单片字长”。

    $$
    \text{字向片数} = \frac{\text{目标字数}}{\text{单片字数}}
    $$

    $$
    \text{位向片数} = \frac{\text{目标字长}}{\text{单片字长}}
    $$

    $$
    \text{总片数} = \text{字向片数} \times \text{位向片数}
    $$

    | 扩展方式   | 目的              | 连接要点                            |
    | ------ | --------------- | ------------------------------- |
    | 位扩展    | 增加存储字长          | 各芯片的地址线、片选和读写控制线并联，数据线分别连接不同数据位 |
    | 字扩展    | 增加存储单元数量，扩大地址空间 | 各芯片的数据线并联，使用高位地址译码产生片选信号        |
    | 字位同时扩展 | 同时增加字数和字长       | 先用位扩展组成一组，再用字扩展增加芯片组数量          |

    <Warning>
      不要把“字扩展”和“位扩展”的方向弄反：字扩展增加“有多少个字”，位扩展增加“每个字有多少位”。
    </Warning>
  </Accordion>

  <Accordion title="扩展计算示例">
    用 $4\text{K} \times 8$ 位的芯片组成 $16\text{K} \times 16$ 位的主存。

    * 位向片数：$16 \div 8 = 2$
    * 字向片数：$16\text{K} \div 4\text{K} = 4$
    * 芯片总数：$2 \times 4 = 8$

    目标主存共有 $16\text{K}$ 个存储单元，因此需要 14 根地址线；每个存储字为 16 位，因此需要 16 根数据线。
  </Accordion>
</AccordionGroup>

### 多模块存储器

<AccordionGroup>
  <Accordion title="连续编址与交叉编址">
    | 编址方式       | 模块号地址位 | 连续地址的分布       | 主要特点                      |
    | ---------- | ------ | ------------- | ------------------------- |
    | 连续编址（高位交叉） | 高位地址   | 连续地址位于同一模块    | 顺序访问难以让多个模块并行工作，主要作用是扩展容量 |
    | 交叉编址（低位交叉） | 低位地址   | 连续地址依次分布到不同模块 | 可利用流水方式连续启动不同模块，提高存储器吞吐率  |

    对按字编址的交叉存储器，若共有 $m$ 个模块，则：

    $$
    \text{模块号} = \text{单元地址} \bmod m
    $$

    ```text title="交叉编址示例" theme={null}
    m = 4
    地址 0、4、8  → 模块 0
    地址 1、5、9  → 模块 1
    地址 2、6、10 → 模块 2
    地址 3、7、11 → 模块 3
    ```
  </Accordion>

  <Accordion title="轮流启动与同时启动">
    **轮流启动方式**

    设模块存取周期为 $T$，数据总线传送一个字的周期为 $r$。为了避免下一个模块的数据已经准备好时总线仍被占用，模块数应满足：

    $$
    m \geq \frac{T}{r}
    $$

    若题目要求最小整数模块数，则应取：

    $$
    m_{\min} = \left\lceil \frac{T}{r} \right\rceil
    $$

    连续读取 $m$ 个字所需时间为：

    $$
    t_m = T + (m-1)r
    $$

    <Info>
      第一个字需要等待模块完成一次存取，后续 $m-1$ 个字可以按总线周期依次传送。
    </Info>

    **同时启动方式**

    * 多个模块同时开始读或写。
    * 所有模块一次并行读写的总位数必须能够由存储器数据总线一次传送。
    * 并行访问通常以连续若干字节为单位，并受到数据对齐要求约束。
  </Accordion>

  <Accordion title="为什么交叉编址能提高吞吐率">
    连续编址时，连续地址集中在同一模块，CPU 顺序读取时仍需等待该模块完成一次存取，其他模块无法有效接续工作。

    交叉编址时，连续地址分散在不同模块。模块 0 正在存取时，可以启动模块 1；模块 1 正在存取时，可以启动模块 2。各模块的操作在时间上重叠，因此能提高连续访问时的吞吐率。
  </Accordion>
</AccordionGroup>

## 外部存储器

### 磁盘存储器

<AccordionGroup>
  <Accordion title="磁盘设备的组成与存储区域">
    **设备组成：**

    * **磁盘驱动器**：驱动盘片旋转，并控制磁头在盘面上读写数据。
    * **磁盘控制器**：连接磁盘驱动器与主机，接收并解析 CPU 的命令，发送控制信号并监控运行状态。
    * **盘片**：真正保存数据的磁性存储介质。

    **存储区域的层次：**

    ```text theme={null}
    盘片 → 记录面 → 磁道 → 扇区
    不同记录面上的同号磁道构成柱面
    ```

    | 参数    | 含义                   |
    | ----- | -------------------- |
    | 记录面数  | 通常等于磁头数量，每个磁头负责一个记录面 |
    | 柱面数   | 等于单个记录面上的磁道数         |
    | 每道扇区数 | 每条磁道划分为多少个扇区         |
    | 扇区容量  | 每个扇区保存的字节数           |

    <Warning>
      磁盘读写的最小单位通常是扇区。一次读写一个扇区时，即使只需要其中少量字节，也必须访问整个扇区。
    </Warning>
  </Accordion>

  <Accordion title="磁盘地址与寻址顺序">
    磁盘地址通常由“柱面号、盘面号、扇区号”组成：

    | 地址字段顺序 | 柱面（磁道）号 | 盘面（磁头）号 | 扇区号        |
    | ------ | ------- | ------- | ---------- |
    | 作用     | 确定目标磁道  | 确定目标记录面 | 确定磁道中的目标扇区 |

    访问顺序通常为：

    ```text theme={null}
    先移动磁头定位柱面
    → 再选择目标盘面对应的磁头
    → 最后等待目标扇区旋转到磁头下方
    ```

    <Tip>
      同一柱面内更换磁头不需要重新寻道，因此磁盘地址通常先给出柱面号，再给出盘面号。
    </Tip>
  </Accordion>

  <Accordion title="记录密度与容量">
    | 指标  | 含义                     |
    | --- | ---------------------- |
    | 位密度 | 单条磁道单位长度上能够记录的二进制位数    |
    | 道密度 | 沿磁盘半径方向单位长度上的磁道数       |
    | 面密度 | 位密度与道密度的乘积，反映单位面积的存储能力 |

    **非格式化容量**表示磁记录表面理论上可利用的磁化单元数量：

    $$
    C_{\text{非格式化}} = \text{记录面数} \times \text{柱面数} \times \text{每磁道磁化单元数}
    $$

    **格式化容量**表示按扇区、地址和校验信息等格式组织后，实际可供用户使用的容量：

    $$
    C_{\text{格式化}} = \text{记录面数} \times \text{柱面数} \times \text{每道扇区数} \times \text{每扇区字节数}
    $$

    <Info>
      对低密度磁盘，各磁道扇区数相同，由于外圈周长更长，内圈位密度通常高于外圈。高密度磁盘则尽量使各磁道位密度接近，外圈可设置更多扇区，从而提高整盘容量。
    </Info>
  </Accordion>

  <Accordion title="磁盘存取时间的计算">
    磁盘的完整响应时间通常包括：

    $$
    T_{\text{响应}} = T_{\text{排队}} + T_{\text{控制器}} + T_{\text{寻道}} + T_{\text{旋转等待}} + T_{\text{传输}}
    $$

    磁盘存取时间通常只计算后三项：

    $$
    T_{\text{存取}} = T_{\text{寻道}} + T_{\text{旋转等待}} + T_{\text{传输}}
    $$

    设磁盘转速为 $n$ 转/分钟，则旋转一周的时间为：

    $$
    T_{\text{旋转}} = \frac{60}{n}\text{ s}
    $$

    平均旋转等待时间约为旋转半周：

    $$
    T_{\text{旋转等待平均}} = \frac{30}{n}\text{ s}
    $$

    平均存取时间一般写为：

    $$
    T_{\text{存取平均}} = T_{\text{寻道平均}} + T_{\text{旋转等待平均}} + T_{\text{传输}}
    $$

    <Tip>
      计算题中若给出“平均寻道时间”，应直接使用；若给出“最大寻道时间”和“最小寻道时间”，再根据题意判断应取平均值还是最坏值。
    </Tip>
  </Accordion>

  <Accordion title="RAID 各级对比">
    | 级别    | 数据组织与冗余方式            | 主要特点                             |
    | ----- | -------------------- | -------------------------------- |
    | RAID0 | 条带化，无冗余、无校验          | 容量利用率高、读写性能高，但任何一块磁盘故障都会丢失数据     |
    | RAID1 | 镜像盘，每个数据盘对应一个镜像盘     | 可靠性高、读性能较好，写需要更新多个副本，磁盘利用率仅为 50% |
    | RAID2 | 按位交叉并使用海明码纠错，需要多个校验盘 | 可纠正一位错、检测两位错，但冗余开销大，现已很少使用       |
    | RAID3 | 按位或字节交叉，使用一个专用校验盘    | 连续传输率高，但随机小 I/O 性能较差             |
    | RAID4 | 按数据块交叉，使用一个专用校验盘     | 可并行响应多个读请求，但写操作频繁更新校验盘，校验盘容易成为瓶颈 |
    | RAID5 | 数据块交叉，奇偶校验块分散到各磁盘    | 避免专用校验盘瓶颈，允许一块磁盘故障，广泛用于服务器       |
    | RAID6 | 使用两种独立校验信息           | 允许两块磁盘同时故障，可靠性更高，但控制和写入开销更大      |

    <Info>
      RAID7 通常指带有实时操作系统和 Cache 的厂商增强型磁盘阵列，不是与 RAID0～RAID6 完全同维度的标准分级。复习时按指定教材口径掌握，不建议与前面各级只按“冗余方式”横向比较。
    </Info>
  </Accordion>

  <Accordion title="固态硬盘（SSD）">
    固态硬盘基于 Flash 等半导体存储介质，没有机械寻道和旋转等待。

    **基本读写单位：**

    * 读操作以页为单位。
    * 写操作以页为单位。
    * 擦除操作以块为单位，通常一个块包含多个页。

    <Warning>
      Flash 通常需要先擦除再写入，因此会产生写放大。控制器通过闪存转换层（FTL）、磨损均衡和垃圾回收等机制管理物理块，并延长 SSD 的寿命。
    </Warning>
  </Accordion>
</AccordionGroup>

## Cache

### Cache 的基本原理

<AccordionGroup>
  <Accordion title="Cache 行与数据块">
    Cache 以数据块为单位把主存中的连续数据调入高速缓存。主存和 Cache 之间以块为单位交换数据，CPU 与 Cache 之间通常以字或字节为单位访问。

    ```text theme={null}
    主存地址 = 块号 + 块内地址
    Cache 行 = 数据块 + 标记 + 控制信息
    ```

    每一行通常需要：

    | 字段    | 作用                     |
    | ----- | ---------------------- |
    | 数据位   | 保存主存数据块                |
    | 标记位   | 判断 Cache 行中的块是否来自目标主存块 |
    | 有效位   | 判断该行中的信息是否有效           |
    | 脏位    | 回写法中判断该行是否被修改而未写回主存    |
    | 替换信息位 | 记录 LRU、FIFO 等替换算法所需的信息 |

    <Info>
      Cache 利用程序局部性工作：命中时直接返回数据，未命中时再从主存调入数据块。
    </Info>
  </Accordion>
</AccordionGroup>

### 映射方式

<AccordionGroup>
  <Accordion title="直接映射">
    主存中的每个块只能映射到唯一的 Cache 行：

    $$
    \text{Cache 行号} = \text{主存块号} \bmod \text{Cache 总行数}
    $$

    地址结构为：

    | 标记 | Cache 行号 | 块内地址 |
    | -- | -------- | ---- |

    * 查找时只需要比较一个标记，硬件简单、速度快。
    * 映射位置固定，块冲突概率最高，空间利用率最低。
  </Accordion>

  <Accordion title="全相联映射">
    主存中的任意一个块都可以放入任意一个 Cache 行：

    | 标记 | 块内地址 |
    | -- | ---- |

    * 块冲突概率最低，Cache 空间利用率高。
    * 需要同时比较所有行的标记，比较器数量多、硬件开销大。
  </Accordion>

  <Accordion title="组相联映射">
    Cache 先划分成若干组，主存块映射到唯一的一组，但可以放入该组中的任意一行：

    $$
    \text{Cache 组号} = \text{主存块号} \bmod \text{Cache 组数}
    $$

    地址结构为：

    | 标记 | 组号 | 块内地址 |
    | -- | -- | ---- |

    若每组有 $k$ 行，则称为 $k$ 路组相联映射，它是直接映射和全相联映射的折中。

    | 映射方式  | 可放置位置    | 查找代价 | 冲突概率 | 硬件代价 |
    | ----- | -------- | ---- | ---- | ---- |
    | 直接映射  | 唯一一行     | 最低   | 最高   | 最低   |
    | 全相联映射 | 任意一行     | 最高   | 最低   | 最高   |
    | 组相联映射 | 指定组内任意一行 | 居中   | 居中   | 居中   |
  </Accordion>
</AccordionGroup>

### 替换算法

| 算法         | 选择被替换行的依据       | 特点                       |
| ---------- | --------------- | ------------------------ |
| 随机算法 RAND  | 随机选择一行          | 实现简单，命中率不稳定              |
| 先进先出 FIFO  | 替换最早调入的行        | 不考虑最近访问情况，可能出现 Belady 异常 |
| 最近最少使用 LRU | 替换最长时间没有被访问的行   | 利用时间局部性，命中率通常较好，硬件维护开销较大 |
| 最不经常使用 LFU | 替换一段时间内访问次数最少的行 | 考虑访问频率，但频率统计和淘汰策略较复杂     |

<Warning>
  直接映射不需要选择被替换行；全相联和组相联映射在发生冲突时可以按题目指定的算法选择被替换行。
</Warning>

### 写策略

<AccordionGroup>
  <Accordion title="写命中">
    | 策略       | 处理方式            | 主要特点                         |
    | -------- | --------------- | ---------------------------- |
    | 全写法（直写法） | 同时写入 Cache 和主存  | 主存始终有最新数据，一致性简单；写操作频繁时占用主存带宽 |
    | 回写法      | 只修改 Cache，并设置脏位 | 减少写主存次数；替换脏行时需要先写回主存         |
  </Accordion>

  <Accordion title="写未命中">
    | 策略    | 处理方式                       | 常见搭配    |
    | ----- | -------------------------- | ------- |
    | 写分配法  | 先把主存块调入 Cache，再在 Cache 中写入 | 常与回写法搭配 |
    | 非写分配法 | 直接写入主存，不把该块调入 Cache        | 常与全写法搭配 |

    <Warning>
      写分配不是“先写主存，再调入 Cache”。其关键动作是“把块调入 Cache 后写入”。
    </Warning>
  </Accordion>
</AccordionGroup>

### 容量与地址位数

<AccordionGroup>
  <Accordion title="Cache 总容量">
    Cache 数据区的行数可由容量和块大小确定：

    $$
    \text{Cache 行数} = \frac{\text{Cache 数据区容量}}{\text{每个数据块的大小}}
    $$

    若题目要求计算包括目录信息在内的总容量，应把每行的数据位、标记位和控制位一起计入：

    $$
    C_{\text{Cache}} = \text{行数} \times \left(\text{数据位} + \text{标记位} + \text{有效位} + \text{脏位}\right) + C_{\text{替换信息}}
    $$

    <Info>
      脏位只在回写策略中存在；替换信息不一定按行计算，可能按组或每种算法单独维护。LRU 位宽必须以教材或题目给出的实现为准，不能一律写成 $\log_2(\text{组内块数})$。
    </Info>
  </Accordion>

  <Accordion title="地址字段计算">
    设主存地址为 $A$ 位，数据块大小为 $B$ 字节，Cache 数据区大小为 $C$ 字节。

    * 块内地址位数：

    $$
    b = \log_2 B
    $$

    * Cache 数据块总数与行数：

    $$
    L = \frac{C}{B}
    $$

    * 直接映射的 Cache 行号位数：

    $$
    r = \log_2 L
    $$

    * $k$ 路组相联的组数为 $S=L/k$，组号位数为：

    $$
    s = \log_2 S
    $$

    * 直接映射的标记位数：

    $$
    t = A - b - r
    $$

    * 组相联映射的标记位数：

    $$
    t = A - b - s
    $$

    * 全相联映射的标记位数：

    $$
    t = A - b
    $$
  </Accordion>

  <Accordion title="地址划分示例">
    某计算机主存地址为 32 位，Cache 数据区容量为 $32\text{ KB}$，块大小为 $64\text{ B}$，采用四路组相联映射。

    1. 块内地址位数：$64=2^6$，所以块内地址为 6 位。
    2. Cache 数据块数：$32\text{ KB} \div 64\text{ B}=512$。
    3. Cache 组数：$512 \div 4=128=2^7$，所以组号为 7 位。
    4. 标记位数：$32-6-7=19$ 位。

    地址结构为：

    | 标记 19 位 | 组号 7 位 | 块内地址 6 位 |
    | ------- | ------ | -------- |
  </Accordion>
</AccordionGroup>

### Cache 性能计算

<AccordionGroup>
  <Accordion title="命中率与平均访问时间">
    设命中率为 $H$，命中时间为 $t_{\text{hit}}$，未命中需要额外付出缺失代价 $t_{\text{miss\_penalty}}$，则：

    $$
    T_{\text{avg}} = t_{\text{hit}} + (1-H) \cdot t_{\text{miss\_penalty}}
    $$

    例如，Cache 命中时间为 1 个时钟周期，缺失代价为 100 个时钟周期，命中率为 95%，则：

    $$
    T_{\text{avg}} = 1 + 0.05 \times 100 = 6
    $$

    平均每次访问需要 6 个时钟周期。

    <Warning>
      要先看清题目给出的是“未命中时的总时间”还是“未命中相对于命中额外增加的代价”，两种写法代入方式不同。
    </Warning>
  </Accordion>
</AccordionGroup>

## 虚拟存储器

### 页式虚拟存储器

<AccordionGroup>
  <Accordion title="分页与地址结构">
    虚拟存储器把程序划分成固定大小的页，把主存划分成同样大小的页框或物理页。页和页框大小相同，因此页内偏移在地址转换前后保持不变。

    | 虚拟地址 | 虚拟页号 VPN | 页内地址 |
    | ---- | -------- | ---- |

    | 物理地址 | 物理页号 PPN | 页内地址 |
    | ---- | -------- | ---- |

    虚拟页号到物理页号的映射由页表保存。页表项通常还包含有效位、访问位、修改位、保护位等信息。

    <Info>
      页内地址字段的位数由页大小决定。虚拟地址和物理地址的页内偏移相同，因此地址转换主要替换页号部分。
    </Info>
  </Accordion>

  <Accordion title="TLB 与地址转换过程">
    快表（TLB）是由高速 SRAM 构成的地址转换缓存，它缓存的是页表项，不是程序数据。

    一次访问大致经过以下步骤：

    1. CPU 用虚拟页号查询 TLB。
    2. TLB 命中时，直接获得物理页号，与页内地址拼接得到物理地址。
    3. TLB 未命中时，访问主存中的页表。
    4. 页表项有效时，把页表项填入 TLB，再完成地址转换。
    5. 页表项无效时产生缺页异常，由操作系统把页面调入主存，更新页表和 TLB，再重新执行相关指令。

    <Warning>
      TLB 命中只说明“地址转换信息已命中”，不代表 Cache 中的数据一定命中。TLB 未命中也不一定发生缺页，还可能只是在页表中找到但 TLB 中没有缓存。
    </Warning>
  </Accordion>

  <Accordion title="TLB 与多级页表">
    若题目给出 TLB 命中率和主存访问时间，可按访问路径计算有效访问时间。

    不访问 Cache 时，设 TLB 查询时间为 $t_{\text{TLB}}$，一次主存访问时间为 $t_m$，TLB 命中率为 $h$：

    $$
    T_{\text{EAT}} = h(t_{\text{TLB}} + t_m) + (1-h)(t_{\text{TLB}} + 2t_m)
    $$

    其中 TLB 未命中时需要访问主存中的页表取得页表项，再访问主存取得目标数据。

    多级页表通过分级存储页表项，避免为整个虚拟地址空间准备一张连续大页表。二级页表的虚拟地址可以写成：

    | 一级页号 | 二级页号 | 页内地址 |
    | ---- | ---- | ---- |

    具体页号位数由虚拟地址位数、页大小和每级页表项数量共同决定。
  </Accordion>
</AccordionGroup>

### 段式与段页式虚拟存储器

<AccordionGroup>
  <Accordion title="段式虚拟存储器">
    段是按照程序的逻辑结构划分的可变长区域。由于段长度可变，段表项除了给出段基址，还必须给出段长，并在访问时进行越界检查。

    | 虚拟地址 | 段号 | 段内地址 |
    | ---- | -- | ---- |

    段式管理便于按程序逻辑共享和保护，但会产生外部碎片。

    <Tip>
      考试中要区分“段内地址”和“页内地址”：段内地址的位数取决于段的最大长度，页内地址的位数取决于页大小。
    </Tip>
  </Accordion>

  <Accordion title="段页式虚拟存储器">
    段页式管理先把程序按逻辑划分成段，再把每个段划分成固定大小的页。

    ```text theme={null}
    虚拟地址 = 段号 + 段内页号 + 页内地址
    ```

    地址转换过程通常为：

    1. 用段号查询段表，得到该段的页表起始地址。
    2. 用段内页号查询页表，得到物理页号。
    3. 将物理页号与页内地址拼接，得到物理地址。

    段页式结合了分段便于逻辑管理和分页减少外部碎片的优点，但需要多次查表，地址转换开销较大。
  </Accordion>
</AccordionGroup>

## 公式与易错点速查

<AccordionGroup>
  <Accordion title="核心公式">
    | 主题            | 公式                                                                |
    | ------------- | ----------------------------------------------------------------- |
    | 命中率           | $H = N_{\text{hit}} / N_{\text{total}}$                           |
    | 平均访问时间        | $T_{\text{avg}} = t_{\text{hit}} + (1-H)t_{\text{miss\_penalty}}$ |
    | 主存芯片总数        | 字向片数 × 位向片数                                                       |
    | 交叉模块数         | $m \geq T/r$，最小整数为 $\lceil T/r \rceil$                            |
    | 连续读取 $m$ 个字   | $t_m = T + (m-1)r$                                                |
    | 平均旋转等待        | $30/n$ 秒，$n$ 的单位为转/分钟                                             |
    | 平均磁盘存取时间      | 平均寻道时间 + 平均旋转等待时间 + 传输时间                                          |
    | Cache 行数      | Cache 数据区容量 / 块大小                                                 |
    | 直接映射 Cache 行号 | 主存块号 mod Cache 总行数                                                |
    | 组相联 Cache 组号  | 主存块号 mod Cache 组数                                                 |
    | 直接映射标记位数      | 地址位数 - 块内地址位数 - 行号位数                                              |
    | 组相联标记位数       | 地址位数 - 块内地址位数 - 组号位数                                              |
  </Accordion>

  <Accordion title="最易混淆的点">
    * **SRAM 与 DRAM**：SRAM 不需要刷新，DRAM 是破坏性读出并需要定时刷新。
    * **字扩展与位扩展**：字扩展增加存储单元数量，位扩展增加每个存储单元的数据位数。
    * **连续编址与交叉编址**：连续编址的高位是模块号，交叉编址的低位是模块号。
    * **RAID4 与 RAID5**：RAID4 使用专用校验盘，RAID5 将校验块分散到各磁盘。
    * **写分配与非写分配**：写分配先把主存块调入 Cache，再写入；非写分配直接写主存。
    * **全写与回写**：全写法同时更新 Cache 和主存，回写法只更新 Cache 并依赖脏位。
    * **TLB 与 Cache**：TLB 缓存地址转换信息，Cache 缓存程序数据。
    * **TLB 未命中与缺页**：TLB 未命中只表示转换项不在 TLB 中；只有页不在主存时才是缺页。
  </Accordion>
</AccordionGroup>
