在SSD

时间:2016-04-23 20:22:31

标签: c++ linux filesystems ssd

我想知道登录SSD的最佳方式是什么。想象一下像数据库日志这样的东西,你只写附加,但你也必须fsync()每个事务或少数事务,以确保应用程序级数据的持久性。

我将介绍一下SSD的工作原理,所以如果你已经知道了这一切,那么无论如何我都应该略读它以防万一我错了。进一步阅读的一些好东西是Emmanuel Goossaert 6-part guide to coding for SSDs和论文Don't Stack your Log on my Log [pdf]

SSD仅在整页中进行写入和读取。页面大小因SSD而异,但通常为4kb的倍数。我的三星EVO 840使用了8kb的页面大小(顺便提一下,Linus calls "unusable shit"以他通常丰富多彩的方式。)SSD无法就地修改数据,它们只能写入空闲页面。因此,结合这两个限制,更新我的EVO上的单个字节需要读取8kb页面,更改字节,并将其写入新的8kb页面并更新FTL页面映射(ssd数据结构),以便该页面的逻辑地址正如操作系统所理解的那样,现在指向新的物理页面。因为文件数据在相同的擦除块(可以擦除的最小页面组)中也不再是连续的,所以我们也在构建一种碎片债务形式,这将使我们在未来的SSD垃圾收集中付出代价。非常低效。

  

作为助手,查看我的PC文件系统:C:\WINDOWS\system32>fsutil fsinfo ntfsinfo c:它有512字节的扇区大小和4kb的分配   (簇的大小。两者都不映射到SSD页面大小 - 可能   效率不高。

有一些问题,只需用例如pwrite()到内核页面缓存并让操作系统处理写出内容。首先,在调用sync_file_range()实际启动IO之后,您需要发出额外的pwrite()调用,否则它将一直等到您调用fsync()并释放IO风暴。其次fsync() seems to block将来对同一文件的write()进行调用。最后,你无法控制内核如何将内容写入SSD,它可能做得很好,或者它可能做得不好导致大量的写入放大。

由于上述原因,并且因为我还需要AIO来读取日志,所以我选择使用O_DIRECT和O_DSYNC写入日志并完全控制。

据我了解,O_DIRECT要求所有写入与扇区大小和整个扇区对齐。因此,每当我决定向日志发出附加内容时,我需要在末尾添加一些填充以将其添加到整个扇区(如果所有写入始终是整个扇区,则它们也将正确对齐,至少在我的代码中。)好吧,那不是那么糟糕。但我的问题是,围绕一大堆SSD页面而不是扇区不是更好吗?据推测,这会消除写入放大?

这可能会占用大量空间,特别是如果一次向日志中写入少量数据(例如几百个字节)。这也可能是不必要的。像三星EVO这样的SSD有写缓存,它们不会在fsync()上刷新它。相反,他们依靠电容器在断电时将缓存写入SSD。在这种情况下,SSD可能做正确的事情,只有一个附加日志只是一次写入扇区 - 它可能不会写出最后的部分页面,直到下一个附加到达并完成它(或除非它被强制淘汰)由于大量不相关的IO导致的缓存。)由于答案可能因设备和文件系统而异,有没有办法可以编写这两种可能性并测试我的理论?有哪些方法可以测量写入放大或Linux上更新/ RMW页面的数量?

1 个答案:

答案 0 :(得分:1)

我会尝试回答你的问题,因为我有相同的任务但是在SD卡中,它仍然是闪存。

简答

您只能在闪存中写入512字节的整页。鉴于闪存的写入次数较差,内核正在缓冲以提高驱动器的使用寿命。

要在闪存中写入一个位,必须先擦除它所在的整个页面。因此,如果要将1个字节附加或修改为已写入的400字节页面,内核基本上会执行:

  • 将整个页面读入缓冲区
  • 使用添加的内容修改缓冲区
  • 删除整页
  • 使用修改后的缓冲区重写整个页面

长答案

扇区(页面)基本上是闪存实现和闪存物理驱动程序的硬件,你无法控制。每次更改内容时都必须清除并重写这些页面。

正如您可能已经知道的那样,如果不清除并重写整个512字节,则无法在页面中重写单个位。现在,闪存驱动器的写周期寿命约为100'000,然后才能损坏扇区。为了改善生命周期,通常是物理驱动程序,有时系统会有一个写入随机化算法,以避免总是写同一扇区。

关于群集,这是在与文件系统相关的更高级别处理的,并且您可以控制它。通常在格式化新硬盘时,可以选择群集大小,在Windows上指的是格式窗口的分配单元大小。

Fat 32 format

据我所知,大多数文件系统都使用位于磁盘开头的索引。该索引将跟踪每个群集以及分配给它的内容。这意味着文件将占用至少1个扇区,即使它要小得多。

FAT32

现在,您的行业规模越小,您的指数表就越大,占用空间就越大。但如果你有很多小文件,那么你将拥有更好的职业空间。

另一方面,如果您只存储大文件,并且想要选择最大的扇区大小,则只略高于文件大小。

由于您的任务是执行日志记录,我建议您登录扇区大的单个巨大文件。尝试过这种类型的日志后,在单个文件夹中包含大量文件可能会导致问题,尤其是在嵌入式设备中。

实施

现在,如果您具有对驱动器的原始访问权限并且想要真正优化,则可以直接写入磁盘而无需使用文件系统。

在好的方面 *将为您节省相当多的磁盘空间 *如果您的设计足够智能,将在出现故障时使磁盘容忍 *如果您使用的是有限的系统,将需要更少的资源

缺点 *更多的工作和调试 *系统无法识别驱动器。

如果您只记录,则不需要拥有文件系统,只需要一个指向要写入数据的页面的入口点,这将不断增加。

我在SD卡上的实现是在闪存的乞讨时保存100页以存储有关写入和读取位置的信息。这是在一个页面中保存的,但是为了避免内存周期问题,我将在100页上按顺序写入循环方法,然后使用算法检查哪个是包含最新信息的最后一个。

写入位置存储每5分钟左右完成一次,这意味着如果断电,我只会丢失5分钟的日志。在进一步写入之前,也可以从最后一个写入位置检查其他扇区是否包含有效数据。

这提供了一个非常强大的解决方案,因为表格损坏的可能性非常小。

我还建议缓冲512个字节并逐页写入。

其他

您可能还想检查一些特定于日志的文件系统,他们可能只是为您完成这项工作:Log-structured file system