《Operating System:Three Easy Pieces》阅读笔记<三十一>—SSD磁盘系统

85 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

SSD磁盘系统

随着电子技术的不断发展,一种名为闪存的新型存储技术越来越重要,基于这种技术设计的存储设备称为固态存储,和CPU一样,这种电子存储设备建造在复杂的晶体管电路之上,这种基础硬件架构的不同使得闪存设备有一些机械硬盘设备没有的特性:1.要写入给定的块,必须要擦除更大的块 2.过于频繁的在页面上写入会导致晶体管寿命减少(不可靠)

不同闪存技术的区别主要在于单位颗粒内的存储位数,主流的闪存技术有SLC、MLC、TLC和QLC,分别对应单位颗粒内存储位一位、两位、三位和四位,存储位每上升一位,对存储大小的提升都十分大,但是相应的传输带宽越小、存储寿命越短。

附上三种颗粒的各种操作开销对比

大量的闪存颗粒组成的存储设备就是SSD,对于操作系统来说,SSD同样是大块的存储空间。而在底层的管理上,SSD有其特殊的概念,首先是将存储空间分成两个等级,一是可擦除的块,二是可访问的页。

闪存中的块、页的概念和磁盘、虚拟内存中的都不一样

一般块大小比页大小要大很多,例如块为256KB的话,页大小就是4KB。对块和页的操作就是SSD的基本操作,有以下三种:

  • 读取(对页):客户端输入读取命令和适当的页码,就可以实现对任意块的读取,这个操作只需要几十微秒,这意味着SSD是随机访问的设备
  • 擦除(对块):写入页面之前,设备的性质要求首先擦除页面所在的整个块,因此,在执行擦除之前,您必须确保块中所关心的任何数据都已经复制到其他地方(内存,或者可能是另一个闪存块)。擦除块十分昂贵,通常需要几毫秒。
  • 编程(对页):一旦一个块被擦除,程序命令就可以用来将页面中的一些1改为0,并将页面中需要的内容写入闪存中。编程一个页面比擦除一个块要便宜,但比读取一个页面要贵,在现代闪存芯片上通常需要一百微秒左右。

每一个页面都有与之相关的状态,页面以INVALLD开始,还有ERASED、VALID,已一个四个页大小的块为例,如下图所示。

注意page2,3,4的数据要保存到其它块中

总的来说,SSD是一种性能很高的磁盘(尤其在随机读性能上)。但是它有两个最大的问题,一是写入性能,因为在写入之前擦除操作是必须的,所以我们如果只写入一页就擦除整个块的话,那写入速度将会慢的离谱,二是可靠性,因为闪存颗粒的擦除次数是有限的,所以太过频繁的擦除会造成芯片很快的磨损。另外除了擦除,还有干扰的问题,即访问闪存中的特定页面时,相邻页面中的一些位可能会翻转,这也是我们需要解决的一个问题。

再讨论解决上述问题之前,我们先思考如何将那么多的闪存颗粒组织起来称为可用的SSD,简单来说,SSD由标准存储接口、闪存芯片(内含一大堆的闪存颗粒)、内存、一个称为FTL的固件区域。它们的作用大概可以这样概括:

  • 存储接口:不知道是啥
  • 闪存芯片:简单来讲就是大量的闪存颗粒的聚合体,现代SSD内部通常会有多个闪存芯片,并发执行获得更高的性能。
  • 内存:用于缓存数据和存储映射表
  • FTL:获取设备接口上的数据访问请求,并转化为物理块和物理页面上的读取、擦除、编程命令。

它们之间的组成关系如下图所示

现在我们来看FTL是怎样工作的,对于FTL来说,每一个请求都要落实到块或页上,因此需要建立一个逻辑-物理的地址映射。最直接也是最低效的方式是直接映射,逻辑页面N的读取直接映射到物理页面N的读取。可想而知,对于外部请求,它直接读取整个数据块,擦除整个数据块,对整个数据块进行编程,非常之低效。更严重的是可靠性问题,擦除次数多不只是低效,而且意味着更高的损耗。

很多人可能想到了,目前最适合SSD的映射是日志结构的映射,因为在写入数据的时候,大段的写入对SSD来说更加友好。还是用一个例子来看,输入如下图所示

可看到,虽然是对四个不同位置逻辑块的写入,但是物理块上它们是连续写入的。

大多数SSD会按照块内顺序写入页面,从而减少与程序干扰相关的可靠性问题

那么如何对磁盘进行读取呢,进一步将,FTL如何将逻辑块请求的地址转化为物理块请求的地址,这要依靠一种称为映射表的结构,映射表存储在磁盘内存中,它保存着逻辑地址和物理地址的映射关系,如下图所示。

这种日志结构的基本方法有一些缺点,首先,逻辑块的覆盖会导致我们称之为垃圾的东西,过多的垃圾收集会增加写入放大,设备必须周期性地执行垃圾收集(GC),以找到所述的块并为将来的写释放空间;第二个是内存映射表的高成本,设备越大,这样的表需要的内存就越多。

写入放大是闪存 和 固态硬盘 (SSD)中一种不良的现象,即实际写入的物理数据量是写入数据量的多倍。

我们先看第一个问题,假如在块中有两个页更新,两个新页写入新块,则旧块中的数据变成垃圾数据,如果想对这些数据进回收,则要进行三步操作:

可以看到,垃圾回收的成本很高,需要读取和重写整个块,所以我们要尽量减少垃圾回收的次数,现代SSD利用overprovision方法,overprovision就是磁盘实际容量比标记容量高一些,用于推迟GC,增加内部带宽

记录某个页是否能够垃圾回收需要用到一个新的API trim,trim API取一个地址和一段长度,简单的通知设备由该地址和长度指定的块已经被删除

现在我们看第二个问题,简单的说,页面级粒度的映射表是不实际的,因为哪怕只是一个1tb的SSD,最高就会有1GB的映射表。一种粒度更大的映射关系是块映射,要做到块映射,逻辑空间的索引也要变成块号+偏移量,这样在FTL中只需要在内存中读取块与块的映射即可,但是出于性能原因,在基于日志的FTL中使用基于块的映射并不能很好地工作。当发生small write(即小于物理块大小的写)时,FTL必须从旧块中读取大量的实时数据,并将其复制到一个新块中(以及小写入中的数据)。这种数据复制大大增加了写放大,从而降低了性能。

我们可以采用一种混合的方法,具体为,FTL在其内存中存储两种类型的映射表,一种是日志表,以页为单位映射,一种是数据表,以块为单位映射。当寻找特定的逻辑块时,FTL首先查阅日志表,找不到就查询数据表。

它们之间的关系是,当日志表超过一定大小的时候,就将在同一块内的映射转化到数据表中,这一操作称为switch merge。如下图所示

如果有大量的随机小写,则会出现什么情况?

混合的方法虽然好,但是比较复杂,可能是简单的方法就是只在内存中缓存FTL的活动部分,从而减少所需的内存量,大部分情况下这种方法表现的很好。

擦除均衡也是SSD的工作过程中很重要的一环,多次擦除/编程周期会损害闪存块,对于哪些长时间都不会被覆盖的数据,需要定期的进行重写,来平衡损耗。

现在我们已经介绍了SSD的大部分内容,可以做出一些总结,SSD是随机存取的设备,和机械硬盘最大的区别在于随机读写,对于随机读写IO,机械硬盘的每秒只能做到几百次IO,但固态硬盘可以做到更好。下图是不同硬盘之间的性能比较图。