前两篇,我们已经把视角从 NAND 转向了 SSD Firmware。
几颗 NAND 焊接在一起,并不能直接变成一块可以被电脑使用的 SSD。因为 NAND 本身有很多限制:只能按 Page 写入、按 Block 擦除,已经写过的 Page 不能直接覆盖;Block 有有限的擦写寿命,还可能存在坏块和数据错误。
上一篇我们也讲到,现代 SSD Firmware 已经不再只是一个简单的命令翻译器,而是越来越像一套运行在 SSD 内部的小型操作系统。
那么,在这套 Firmware 中,究竟是谁负责管理 NAND,并把主机发来的读写请求真正执行下去?
答案就是 FTL。
FTL 的全称是 Flash Translation Layer,通常翻译为闪存转换层。
它位于主机接口和底层 NAND 之间,是 SSD Firmware 中最核心的软件层之一。
一、FTL 最重要的职责,是完成读写
从主机的角度看,SSD 最重要的工作其实只有两件:
把数据写进去。
把数据读出来。
主机发来一笔 Write,SSD 就要把数据正确保存下来。
主机发来一笔 Read,SSD 就要把之前保存的数据正确返回。
因此,无论 FTL 内部有多少算法、数据结构和后台任务,它最重要的职责始终是:
组织数据在 Host 和 NAND 之间流动,让主机的数据能够正确写入,也能够正确读出。
读取时,FTL 要根据主机给出的逻辑地址,找到数据真正存放的物理位置,再组织后续的 NAND Read。
写入时,FTL 要为新数据选择合适的物理空间,组织 NAND Program,并在写入完成后记录数据的新位置。
所以,FTL 不只是一个地址翻译器。
更准确地说:
FTL 是 SSD 读写数据通路的核心管理层。
不过,NAND 并不是一种可以随意读写的介质。
为了让读写长期、稳定地进行,FTL 还必须管理地址、坏块、空间、寿命和各种异常情况。
二、地址管理:数据到底放在哪里?
主机访问 SSD 时,使用的是 LBA,也就是逻辑块地址。
例如,主机可能发来这样的请求:
读取 LBA 1000。
把新数据写到 LBA 2000。
但是在 NAND 内部,并不存在一个固定位置天然就叫“LBA 1000”。数据真正存放在某颗 NAND 的某个物理 Page 中。而且,同一个 LBA 对应的物理位置还会不断变化。
主机重新写入同一个 LBA 时,新数据通常会被写到新的物理位置;GC 搬迁数据时,物理位置也会改变;原来的 Block 出现问题时,数据还可能被转移到其他位置。
因此,FTL 必须维护逻辑地址与物理地址之间的对应关系。这就是 L2P,也就是 Logical to Physical Mapping。
读取时,FTL 通过 L2P 找到数据所在的位置。
写入完成后,FTL 又要更新 L2P,记录数据的新位置。
所以,地址翻译虽然不是 FTL 的全部,却是所有读写能够进行的基础。
没有 L2P,读取不知道数据在哪里,写入之后也无法记住数据被放到了哪里。
三、坏块管理:先确定哪些空间不能使用
NAND 允许存在坏块。
有些 Block 在出厂时就已经被 NAND 厂商标记为坏块,通常称为 Factory Bad Block。
还有一些 Block 会随着使用和老化,在 Program 或 Erase 过程中逐渐暴露出问题,成为新增坏块。
FTL 需要记录这些坏块,并将它们从正常的物理空间中排除。
在为一笔写入分配位置之前,FTL 首先要确保目标 Block 是可以正常使用的。已经被标记为坏块的区域,不能再参与后续的数据写入。
如果一个正在使用的 Block 出现故障,FTL 还可能需要尽量读出其中仍然有效的数据,将数据搬迁到健康位置,再更新L2P映射并隔离原来的 Block。
因此,坏块管理首先解决的是:
哪些物理空间可以继续使用?
哪些物理空间已经不能再信任?
原来存放在故障位置的数据应该怎么办?
只有先排除这些不可靠的区域,FTL 才能继续进行后面的空间分配。
四、空间分配:新数据应该写到哪里?
确定哪些 Block 可以使用以后,FTL 还要从健康的物理空间中,为新数据选择一个合适的位置。
主机可以反复写入同一个 LBA,但 NAND Page 不能直接原地覆盖。
假设 LBA 1000 原来的数据存放在物理 Page A。
现在主机再次写入 LBA 1000。
FTL 通常不会直接修改 Page A,而是选择一个新的可写 Page B,把新数据写进去。
写入成功后,FTL 将映射关系从:
LBA 1000 → Page A
更新为:
LBA 1000 → Page B
原来的 Page A 不再代表最新数据,因此会变成 Invalid Page。
所以,主机眼中的一次覆盖写,在 SSD 内部实际上是:
分配新的物理空间,写入新数据,更新映射,并让旧位置失效。
但空间分配并不是随便找到一个空 Page 就结束了。
FTL 还要考虑:
目标 Block 是否健康,Page 是否处于可写状态,当前还剩多少空闲空间,不同 Block 的磨损程度如何,以及怎样安排数据才能兼顾性能和寿命。
在更复杂的 SSD 中,FTL 还会尽可能利用 NAND 的并行能力,提高整体读写效率。
至于 Channel、Die 和 Plane 之间怎样形成并行操作,后面可以再单独展开。
五、垃圾回收:失效空间怎么重新利用?
随着主机不断更新数据,NAND 中会产生越来越多的 Invalid Page。
但 NAND 不能只擦除某一个 Page,而必须按整个 Block 擦除。
问题在于,一个 Block 里面通常既有已经失效的数据,也有仍然有效的数据。
为了回收这个 Block,FTL 要先把其中的有效数据读出来,再将它们写到其他位置,同时更新对应的 L2P,最后擦除整个旧 Block。
这个过程就是 Garbage Collection,也就是垃圾回收。
GC 的目的并不是单纯整理数据,而是重新获得可以继续写入的空闲 Block。
因此,GC 本质上是在为写入服务。
如果没有 GC,Invalid Page 会不断占用物理空间,空闲 Block 会越来越少,最终新的 Host Write 就找不到可以落盘的位置。
六、磨损均衡:不能总让少数 Block 承受写入
NAND Block 可以承受的 Program/Erase 次数是有限的。
如果 FTL 总是反复使用少数几个 Block,这些 Block 就会提前达到寿命极限,而其他 Block 可能还很少被使用。
因此,FTL 在安排数据写入时,还要考虑不同 Block 的擦写情况,让写入压力尽量不要长期集中在少数区域。
这就是 Wear Leveling,也就是磨损均衡。
磨损均衡并不是要求所有 Block 的 P/E 次数在任何时刻都完全一致,而是尽量避免擦写压力长期集中在少数区域,将盘内各个 Block 的 Erase Count 差距控制在合理范围内,使 NAND 的整体寿命得到更充分的利用。
从当前一笔写入来看,它影响的是:
这次应该选择哪个 Block。
从整个 SSD 的生命周期来看,它决定的是:
这块 SSD 可以稳定使用多久。
所以,磨损均衡仍然是在为写入服务,只不过它考虑的不只是当前这一笔 IO,而是 SSD 的长期寿命。
七、TRIM:哪些数据已经不需要保留?
用户在操作系统中删除一个文件以后,文件系统知道相关逻辑空间已经空闲,但 SSD 不一定立刻知道。
如果主机不进行额外通知,FTL 可能仍然把这些数据当作有效数据。
后续执行 GC 时,FTL 甚至可能继续读取并搬迁这些已经被用户删除的数据。
TRIM,或者 NVMe 中相应的 Deallocate 语义,就是主机告诉 FTL:
这些 LBA 中的数据已经不再需要了。
收到通知以后,FTL 可以解除相应的映射,或者将对应的物理 Page 标记为无效。
这样,后续 GC 就不必再搬迁这些数据。
TRIM 可以减少无意义的内部读写,降低写放大,也能让物理空间更快得到回收。
八、Error Handling:读写并不总能一次成功
NAND 并不是每一次 Read、Program 和 Erase 都一定成功。
读取时,可能因为阈值电压漂移、干扰或老化产生错误。
写入时,可能出现 Program Fail。
擦除时,也可能出现 Erase Fail。
因此,FTL 不仅要组织正常的读写流程,还要根据操作结果进行 Error Handling。
例如,读取出现错误后,FTL 要判断是否继续进入 ECC 纠错、Read Retry、Soft Read,甚至 RAID 恢复。
写入失败后,FTL 可能需要重新选择一个健康的物理位置,将数据再次写入,并更新新的映射关系。
擦除失败后,FTL 则可能将这个 Block 标记为坏块,后续不再继续使用。
所以,Error Handling 解决的并不是某一种特定错误,而是:
当正常的数据通路无法继续时,FTL 应该如何恢复、重试或者隔离故障,让这笔 IO 最终得到一个确定结果。
九、掉电恢复:数据写进去了,还要能够重新找到
FTL 管理的不只是用户数据,还有 L2P、坏块表、Block 状态等大量内部元数据。
为了提高访问速度,这些数据的一部分通常会放在 RAM 中。
但是 RAM 掉电以后就会丢失。
假设用户数据已经成功写入 NAND,但新的 L2P 映射还没有完成持久化,此时突然断电。
下一次启动时,就可能出现一种情况:
数据仍然存在,但 Firmware 已经不知道它属于哪个 LBA。
又或者,GC 正在搬迁数据:
旧位置还没有完全释放,新位置刚刚写完,映射也只更新了一部分。
此时突然掉电,下一次上电后,FTL 必须判断新旧两份数据中,哪一份才是有效版本。
因此,FTL 还要保存关键数据,并在重新上电以后恢复到一致状态。
它必须保证:
已经向主机确认完成的写入,在下一次启动以后仍然能够被正确读取。
十、FTL 不是一个单独的算法
现在再回头看,FTL 并不只是一张 L2P 表,也不只是一个地址翻译函数。
它包含的是一整套围绕 NAND 建立起来的管理机制:
地址管理,负责记录数据放在哪里。
坏块管理,负责排除不可靠的物理空间。
空间分配,负责从健康空间中决定新数据写到哪里。
垃圾回收,负责重新获得可以继续写入的空间。
磨损均衡,负责避免少数 Block 提前老化。
TRIM,负责识别已经无须继续保留的数据。
Error Handling,负责处理读写和擦除过程中出现的失败。
掉电恢复,负责保证数据和元数据在突然断电后仍然能够恢复一致。
不同 SSD 厂商、不同主控架构,对这些模块的边界划分可能并不完全相同。
但无论怎样划分,它们最终都围绕同一个目标:
让主机的数据能够正确写入、正确读出,并让这一过程长期、稳定地持续下去。
因此,FTL 最核心的职责是读写。
地址翻译、坏块管理、空间分配、GC、磨损均衡、TRIM 和 Error Handling,都是支撑读写正常运行的基础能力。
这一篇只是从整体上认识 FTL。
接下来,我们再从其中最基础的一项开始:
主机只给出一个 LBA,FTL 到底怎样找到对应的数据?
下一篇,讲 L2P 映射表。
谢谢你看我的文章
。
Alipay
Wechat