存储硬件基础 — 磁盘到底怎么存数据 / Storage Hardware Fundamentals: How Disks Store Data
📅 创建时间:2026-06-17 🏷️ 标签:#存储硬件 #HDD #SSD #NVMe #LBA #4K扇区 📚 前置知识:[[00-overview]]
📋 本章目标
- 理解 HDD 的物理结构——盘片、磁头、磁道、扇区的真实含义
- 理解 SSD 的工作方式——NAND Flash、FTL、GC、写入放大
- 理解 NVMe 为什么比 SATA SSD 快那么多
- 掌握 LBA 寻址——操作系统"看到"的磁盘是什么样的
- 理解 4K 扇区与 512e 模拟的来源和影响
- 读懂
lsblk的每一列输出
第1部分:磁盘长什么样?——两个世界的故事
1.1 一个重要的认知起点
当你在 Linux 终端输入 lsblk,你看到的是一棵树:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 476.9G 0 disk
├─nvme0n1p1 259:1 0 512M 0 part /boot
├─nvme0n1p2 259:2 0 16G 0 part [SWAP]
└─nvme0n1p3 259:3 0 460.4G 0 part /2
3
4
5
这看起来很干净——一个叫 nvme0n1 的设备,切成三个分区。
但这一切都是抽象。在 nvme0n1 这个名称背后,是一个完全不同世界里的物理设备。操作系统做了大量工作,让一块 NAND 芯片阵列表现在我们面前时,就像一块"连续的、可分区的、可读写的区域"。
本章讲的就是这个抽象层下面的真实世界。
第2部分:HDD——机械硬盘的物理世界
2.1 如果你拆开一块 HDD
┌─────────────────────────────────────────────────────────────┐
│ HDD 内部结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 主轴马达 │
│ │ │
│ ┌─────────────●─────────────┐ │
│ │ 盘片 (Platter) │ │
│ │ ┌─────┐ │ ← 多张盘片叠在一起
│ │ ┌────┤ ├────┐ │ │
│ │ │ └─────┘ │ │ │
│ │ │ │ │ │
│ │ │ 磁头臂 │ │ │
│ │ │ (Arm) │ │ │
│ │ │ ●───────│───────│── 磁头 (Head) │
│ │ └──────────────┘ │ 读写数据的部件 │
│ │ │ │
│ └───────────────────────────┘ │
│ │
│ 盘片以 5400/7200 RPM 恒定旋转 │
│ 磁头臂摆动定位到需要的磁道 │
│ 磁头悬浮在盘片上方仅 3 纳米的距离 —— 一张纸厚度的 1/3000 │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2.2 数据怎么组织:CHS 到 LBA
最早访问 HDD 的方法是柱面-磁头-扇区(CHS):
┌─────────────────────────────────────────────────────────────┐
│ CHS 三维寻址 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Cylinder (柱面): 多张盘片同一位置的磁道组成一个柱面 │
│ Head (磁头): 哪个读写头 —— 也就是哪张盘片的哪一面 │
│ Sector (扇区): 磁道上的第几个 512 字节块 │
│ │
│ 盘片俯视图: │
│ │
│ ┌──────────────────────┐ │
│ │ 最外圈: Track 0 │ ← 最外圈,数据密度最低 │
│ │ ┌────────────────┐ │ 但线速度最快 │
│ │ │ Track 1 │ │ │
│ │ │ ┌──────────┐ │ │ │
│ │ │ │ Track 2 │ │ │ ← 越内圈,周长越短 │
│ │ │ │ ●主轴 │ │ │ 单位面积数据密度越高 │
│ │ │ └──────────┘ │ │ │
│ │ └────────────────┘ │ │
│ └──────────────────────┘ │
│ │
│ 一个扇区 = 512 字节 (传统) 或 4096 字节 (高级格式化) │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CHS 有两个致命问题:
- 不同磁道扇区数相同——外圈和内圈物理周长不同,但存一样多的数据,外圈浪费了空间
- 最大只能寻址 ~8GB——CHS 地址空间有限(1024柱面 × 255磁头 × 63扇区 × 512B ≈ 7.8GB)
所以现代磁盘都使用 LBA(Logical Block Addressing,逻辑块寻址):
┌─────────────────────────────────────────────────────────────┐
│ LBA —— 一维线性地址 │
├─────────────────────────────────────────────────────────────┤
│ │
│ CHS: (柱面 100, 磁头 2, 扇区 50) ← 三维坐标 │
│ LBA: 扇区编号 12345678 ← 一个递增数字 │
│ │
│ 磁盘固件内部负责 LBA → 物理位置 的映射 │
│ OS 只知道:"请给我读扇区 12345678" │
│ 而不需要知道数据在哪个盘片、哪个磁道上 │
│ │
│ 这个映射带来了一个关键能力: │
│ → 外圈的扇区可以比内圈多(Zone Bit Recording) │
│ → 坏扇区可以被透明重映射到备用区域 │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2.3 HDD 的性能画像
┌─────────────────────────────────────────────────────────────┐
│ HDD 延迟分析 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 寻道时间 (Seek Time): ~8ms 磁头臂摆动到目标磁道 │
│ 旋转延迟 (Rotational): ~4ms 等待目标扇区转到磁头下 │
│ ───────────────────────────────────────────── │
│ 总机械延迟: ~12ms │
│ │
│ 数据传输速率: ~200 MB/s 顺序读写 │
│ │
│ 关键结论: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ HDD 的瓶颈是机械运动,不是数据传输 │ │
│ │ 随机读写(需要频繁寻道)性能会断崖式下降 │ │
│ │ 这就是为什么文件系统要尽量"顺序布局"数据 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 随机读取:每次 ~12ms → 每秒只能做 80 次左右 │
│ → 随机 IOPS ≈ 80-100 │
│ → 顺序 IOPS ≈ 可以到几百(但远不如 SSD) │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
第3部分:SSD——半导体的"磁盘"
3.1 没有机械部件,那数据怎么存?
SSD 用 NAND Flash 存储数据。核心概念:
┌─────────────────────────────────────────────────────────────┐
│ NAND Flash 层级结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 一个 SSD 内部: │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Channel 0 Channel 1 ... Channel N │ │
│ │ │ │ │ │ │
│ │ ┌──┴──┐ ┌──┴──┐ ┌──┴──┐ │ │
│ │ │Chip │ │Chip │ │Chip │ ← NAND 芯片 │
│ │ └──┬──┘ └──┬──┘ └──┬──┘ │ │
│ │ │ Die │ Die │ Die │ │
│ │ ├─ Plane ├─ Plane ├─ Plane │ │
│ │ │ └─ Block │ └─ Block │ └─ Block │ │
│ │ │ └─ Page │ └─ Page │ └─ Page │ │
│ │ │ │ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 层级关系: │
│ SSD → Channel → Chip → Die → Plane → Block → Page │
│ ↑ ↑ │
│ 擦除单位 读写单位 │
│ (几百KB) (4~16KB) │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
3.2 SSD 的核心矛盾:读写的非对称性
┌─────────────────────────────────────────────────────────────┐
│ NAND Flash 操作的非对称性 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 读 (Read): │
│ 单位: Page (4KB ~ 16KB) │
│ 速度: ~几十微秒 │
│ 可以任意读取,不受限制 │
│ │
│ 写 (Program): │
│ 单位: Page (4KB ~ 16KB) │
│ 速度: ~几百微秒 │
│ 限制: 只能往"已擦除"的 page 写 │
│ │
│ 擦除 (Erase): │
│ 单位: Block (几百KB ~ 几MB,包含数百个 Page) │
│ 速度: ~几毫秒 —— 非常慢! │
│ 限制: 必须先擦除整个 Block,才能重写其中的 Page │
│ │
│ 核心矛盾: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 写一个 4KB 的 Page → 快 │ │
│ │ 覆写一个 4KB 的 Page → 必须先擦除整个 Block → 慢! │ │
│ │ │ │
│ │ SSD 不能像 HDD 那样原地覆盖数据 │ │
│ │ 这就是 FTL (Flash Translation Layer) 存在的根本原因 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
3.3 FTL — SSD 的大脑
FTL = Flash Translation Layer,是 SSD 控制器里的固件。它做三件关键的事:
┌─────────────────────────────────────────────────────────────┐
│ FTL 的三大职责 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 地址映射 (Address Mapping) │
│ │
│ OS 发出的 LBA FTL 维护的映射表 实际的 NAND 位置 │
│ ┌──────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ LBA 100 │ ───────→ │ LBA 100 → │ ───────→ │ Block 5, │ │
│ │ │ │ Block 5, │ │ Page 12 │ │
│ │ │ │ Page 12 │ └──────────┘ │
│ │ │ └──────────────┘ │
│ │ │ │
│ │ OS 覆写 │ 映射表更新! │
│ │ LBA 100 │ ───────→ │ LBA 100 → │ ───────→ │ Block 8, │ │
│ │ │ │ Block 8, │ │ Page 3 │ │
│ │ │ │ Page 3 │ └──────────┘ │
│ │ │ └──────────────┘ │
│ │ │ 旧位置(Block5,Page12)标记为无效 │
│ └──────────┘ │
│ │
│ OS 始终用同一个 LBA 地址读写 │
│ FTL 在背后把 LBA 映射到不同的物理位置(写时重定向) │
│ OS 根本不知道数据实际上搬到了哪里 │
│ │
│ 2. 垃圾回收 (Garbage Collection) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Block X: [有效] [有效] [无效] [无效] [有效] [无效] │ │
│ │ │ │
│ │ GC 过程: │ │
│ │ 1. 选一个"脏"Block(无效页占比高) │ │
│ │ 2. 把其中的有效页复制到另一个 Block │ │
│ │ 3. 擦除整个 Block,使其可重写 │ │
│ │ │ │
│ │ 代价: 写入放大 (Write Amplification) │ │
│ │ → 你只想写 4KB 数据 │ │
│ │ → GC 过程中需要搬运有效数据,实际写了 16KB │ │
│ │ → 写入放大因子 (WAF) = 4x │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 3. 磨损均衡 (Wear Leveling) │
│ │
│ NAND 每个 Block 有擦除次数上限 (~1000~100000 次) │
│ FTL 确保所有 Block 被均匀使用,而不是某个 Block 被写烂 │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
3.4 SSD 的写前擦除 —— 一个直观类比
┌─────────────────────────────────────────────────────────────┐
│ 黑板类比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 把 SSD 的一个 Block 想象成一块黑板: │
│ │
│ 读 = 看黑板上的字 │
│ 可以一行一行地看 (Page 粒度) │
│ │
│ 写 = 在黑板上写字 │
│ 可以在空白处写一行 (Page 粒度) │
│ 但不能在已经写了字的地方直接改写 —— 必须 │
│ │
│ 擦除 = 擦黑板 │
│ 必须整块黑板一起擦 (Block 粒度) │
│ 不能只擦一行 │
│ │
│ 如果你想改一行字: │
│ 1. 在空白的行写新内容 (写新的 Page) │
│ 2. 旧的那行标记为"作废" │
│ 3. 等积攒了足够多作废行后,整体擦除 (GC) │
│ │
│ 这就是 SSD 的核心工作模型 │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
第4部分:NVMe — 为什么比 SATA SSD 快 10 倍
4.1 两个维度:接口协议 vs 物理链路
┌─────────────────────────────────────────────────────────────┐
│ 存储协议栈对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ SATA SSD NVMe SSD │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ 应用层 │ │ 应用层 │ │
│ ├──────────────────┤ ├──────────────────┤ │
│ │ SCSI 命令集 │ │ NVMe 命令集 │ │
│ │ (AHCI 驱动) │ │ (NVMe 驱动) │ │
│ ├──────────────────┤ ├──────────────────┤ │
│ │ SATA 控制器 │ │ PCIe 直连 │ │
│ ├──────────────────┤ │ (没有中间层) │ │
│ │ SATA 物理链路 │ ├──────────────────┤ │
│ │ ~600MB/s 带宽 │ │ PCIe 物理链路 │ │
│ └──────────────────┘ │ ~4~16GB/s 带宽 │ │
│ └──────────────────┘ │
│ │
│ 关键差异: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. 队列深度 (Queue Depth) │ │
│ │ AHCI: 1 个队列,每队列 32 条命令 │ │
│ │ NVMe: 64K 个队列,每队列 64K 条命令 │ │
│ │ │ │
│ │ 这意味着 NVMe 可以同时处理海量并行 IO 请求 │ │
│ │ SSD 内部本来就是多 Channel 并行的 │ │
│ │ SATA/AHCI 的单队列是瓶颈 │ │
│ │ │ │
│ │ 2. 协议栈深度 │ │
│ │ AHCI: 软件 → SCSI 命令 → AHCI 翻译 → SATA 控制器 │ │
│ │ NVMe: 软件 → NVMe 命令 → PCIe 直通 │ │
│ │ │ │
│ │ NVMe 少了两层翻译,延迟更低 │ │
│ │ │ │
│ │ 3. 带宽 │ │
│ │ SATA III: 6Gbps (~600MB/s) │ │
│ │ NVMe PCIe 4.0 x4: ~8GB/s │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
4.2 NVMe 的本质
NVMe 不是一个物理接口 —— 它是一个软件协议规范。
物理接口是 PCIe(你的 nvme0n1 插在主板 M.2 槽上,走 PCIe 总线)
协议是 NVMe(定义了 OS 怎么发命令给 SSD 控制器)2
3
4
4.3 lsblk 输出里的线索
回到开头的 lsblk 输出:
nvme0n1 ← "nvme" 说明是 NVMe 协议
"0" 是 NVMe 控制器编号
"n1" 是 namespace 1(命名空间,NVMe 版的分区概念)
sda ← "sd" 说明是 SCSI/SATA 设备
"a" 是第一块2
3
4
5
6
TYPE 列是 disk 意味着这是一个块设备——操作系统可以对它做随机读写,就像它是一个数组。
第5部分:4K 扇区 — 一个历史遗留问题
5.1 为什么是 512 字节?
┌─────────────────────────────────────────────────────────────┐
│ 512 字节扇区的来历 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1980 年代: 软盘时代,512 字节是一个合理的"最小读写单位" │
│ HDD 沿用这个标准 │
│ │
│ 今天: HDD 数据密度增长了数百万倍 │
│ 但每个扇区还是 512 字节 │
│ │
│ 问题: 每个扇区都需要 ECC 纠错码(额外存储空间) │
│ 512B 扇区 → ECC 占总空间 ~3-5% │
│ 如果用一个 4KB 扇区代替 8 个 512B 扇区 │
│ → ECC 开销降到 ~0.5%,还减少了管理元数据 │
│ │
│ 这就是"高级格式化"(Advanced Format, AF) 的来源 │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
5.2 4K 原生 vs 512e 模拟
┌─────────────────────────────────────────────────────────────┐
│ 4K 原生扇区 vs 512e 模拟 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 4Kn (4K Native): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 物理扇区 = 逻辑扇区 = 4096 字节 │ │
│ │ OS 必须按 4K 对齐读写 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 512e (512 emulation): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 物理扇区 = 4096 字节 │ │
│ │ 逻辑扇区 = 512 字节(固件模拟) │ │
│ │ │ │
│ │ OS 读取 512B: │ │
│ │ 磁盘内部读取整个 4K → 返回其中 512B │ │
│ │ │ │
│ │ OS 写入 512B: │ │
│ │ 磁盘内部: 读整个 4K → 改其中 512B → 写回整个 4K │ │
│ │ 这叫做 Read-Modify-Write (RMW) │ │
│ │ 性能会严重下降! │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 这就是为什么分区要对齐到 1MiB: │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 分区起始 LBA 不对齐 4K: │ │
│ │ 每次 4K 写入跨越两个物理扇区 → 两次 RMW → 慢 │ │
│ │ │ │
│ │ 分区起始 LBA 对齐 4K (LBA % 8 == 0): │ │
│ │ 一次写入命中一个物理扇区 → 直接写 → 快 │ │
│ │ │ │
│ │ 现代 fdisk/gdisk 默认对齐 1MiB (= 2048 个 512B 扇区)│ │
│ │ 1MiB 是 4K 的 256 倍,绝对安全 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
第6部分:其他存储介质速览
6.1 SD 卡 / microSD
本质上也是一个 NAND Flash + 小控制器,类似简配版 SSD。
没有 DRAM 缓存,FTL 功能弱,随机写入性能差。
常用于嵌入式设备(树莓派的系统盘)。
注意:SD 卡的"速度等级" (Class 10/U3/V30) 只标注了
最低连续写入速度,不反映随机 IOPS。2
3
4
5
6
6.2 eMMC
嵌入式 MMC——廉价笔记本和平板常用。
本质上是一个焊接在主板上的 SD 卡升级版。
性能比 SATA SSD 差,没有 NVMe 的并行队列。
lsblk 里显示为 mmcblk0。2
3
4
6.3 UFS
手机里的"NVMe"——性能远超 eMMC。
支持全双工读写、命令队列。
是现代旗舰手机的标配存储方案。2
3
核心总结
总结1:两种存储范式的本质区别
HDD: 机械运动 → 寻道+旋转 → 顺序读写快,随机读写灾难
SSD: 半导体 → 无机械延迟 → 随机读写远超 HDD,但有写入放大问题2
总结2:SSD 的写之谜
SSD 不能覆盖写入 → FTL 重定向 + 垃圾回收 → 写入放大
关键数字:Block (擦除单位) 远大于 Page (读写单位)2
总结3:NVMe 快的两个原因
1. PCIe 直连 —— 带宽是 SATA 的 10+ 倍
2. 64K 深队列 —— 充分利用 SSD 内部并行性
(SATA AHCI 只有 1 个队列 32 条命令)2
3
总结4:4K 扇区的实用意义
分区必须 4K 对齐 → 现代工具默认对齐 1MiB → 你一般不需要操心
但要理解:如果自己算起始扇区,LBA 必须能被 8 整除(512e 场景)2
章节测试
测试1:HDD 瓶颈
为什么 HDD 的随机读写比顺序读写慢几十倍?
测试2:FTL 职责
SSD 的 FTL 做哪三件关键的事?为什么需要 FTL?
测试3:写入放大
什么是写入放大?为什么 GC 会导致写入放大?
测试4:NVMe 优势
NVMe 比 SATA SSD 快的主要原因是什么?(不止一个原因)
测试5:4K 对齐
某分区的起始 LBA 是 63。判断:这个分区是否 4K 对齐?为什么?
参考答案
测试1答案
HDD 随机读写慢是因为每次随机访问都需要机械寻道(~8ms)和旋转延迟(~4ms),总计约 12ms。这个延迟比数据传输本身的时间大得多。顺序读写时磁头不需要移动,瓶颈只是盘片旋转和数据通道
测试2答案
FTL 做三件事:
- 地址映射——把 OS 的 LBA 映射到 NAND 物理位置,支持写时重定向
- 垃圾回收——把"脏"Block 中的有效页搬走,擦除整个 Block
- 磨损均衡——确保所有 NAND Block 的被擦除次数均匀
FTL 存在的原因是 NAND 不能原地覆盖写入——必须整 Block 擦除后才能重写。
测试3答案
写入放大 = 实际写入 NAND 的数据量 / OS 请求写入的数据量。比如 OS 想写 4KB,GC 过程中需要搬运有效数据导致实际写了 16KB,WAF = 4。GC 越频繁,写入放大越严重。
测试4答案
两个维度:
- 物理层:NVMe 走 PCIe 直连,带宽远超 SATA(PCIe 3.0 x4 ≈ 4GB/s vs SATA III ≈ 600MB/s)
- 协议层:NVMe 支持 64K 个队列 × 64K 条命令,充分利用 SSD 内部多 Channel 的并行性;AHCI 只有 1 队列 32 条命令
测试5答案
不 对 齐。LBA = 63,扇区大小如果是逻辑 512B(512e 磁盘),物理扇区是 4K = 8 个逻辑扇区。LBA 63 % 8 = 7 ≠ 0,不对齐。每次跨物理扇区的写入都会触发 Read-Modify-Write。现代工具从 LBA 2048 开始第一个分区就是为了避免这个问题。
相关笔记
- [[02-firmware-bios-uefi]] - 磁盘之后,固件怎么找到启动代码
- [[03-partition-table]] - LBA 地址空间如何被分区表切分
- [[04-filesystem-concepts]] - 文件系统如何建立在块设备之上
下一步学习
- [ ] 阅读 02 - BIOS vs UEFI:固件的本质
学习状态:🟡 开始学习