今天我了解到 FreeBSD 完全取消了对块设备的支持。在阅读他们做出这一决定的理由时,我发现了这一点:
块设备是内核为其提供缓存的磁盘设备。这种缓存使得块设备几乎无法使用,或者至少是危险的不可靠。缓存将重新排序写入操作的顺序,从而使应用程序无法在任何时刻及时了解确切的磁盘内容。这使得磁盘数据结构(文件系统、数据库等)的可预测且可靠的崩溃恢复变得不可能。由于写入可能会被延迟,因此内核无法向应用程序报告哪个特定的写入操作遇到了写入错误,这进一步加剧了一致性问题。
(从https://www.freebsd.org/doc/en_US.ISO8859-1/books/arch-handbook/driverbasics-block.html)
不过我知道 Linux 几乎只使用块设备(尽管可以请求原始设备)。
那么 Linux 如何规避此引文中提到的问题呢?或者大多数驱动程序只是请求原始设备?
答案1
BSD 人真的很硬核,经常做出令人惊讶的事情:-) 在我看来,删除块设备层不是问题(例如,nfs 甚至没有底层块设备),但这种推理并不反对块设备,但反对写缓存。在我看来,删除写入缓存是一件非常糟糕的事情。如果您的进程向磁盘写入了某些内容,那么在没有成功之前您不会收回控制权?
但我不认为他们不知道自己在做什么。希望有人能在另一个答案中解释他们的原因。
为了清楚地解释这一点,我需要解释文件系统是如何工作的。文件系统驱动程序本质上是文件系统操作(目录打开、文件创建、读写、删除等)和块操作(例如:“将页 0xfce2ea31 写出到磁盘块 0xc0deebed”)之间的转换层。
但块操作不会当场到达硬盘。首先,它们将进入块缓存。这意味着,如果文件系统想要将内存页写入磁盘,首先它会写入保留的内存区域。如果内核的内存管理认为这是最佳的,则会将此数据写入硬盘。这可以实现各种速度改进:例如,如果许多写入操作发生在磁盘的开头和结尾,内核可以以这样的方式将它们组合起来,即磁盘头必须尽可能少地重新定位自己。
还有另一个改进:如果您的程序写入文件,它将经历如此快的操作,就好像它是一个 ramdisk 一样。当然,这只有在系统RAM未满时才可能,之后必须等待写缓存清空。但只有当同时存在大量写入操作时才会发生这种情况(例如,您正在复制大文件)。
对于文件系统,在磁盘上运行的文件系统(即块设备)和不在磁盘上运行的文件系统(fe nfs)之间存在很大差异。在第二种情况下,不可能进行块缓存,因为没有块。在他们的例子中,有一个所谓的“缓冲区缓存”,这本质上意味着仍然缓存(读和写),但它不是围绕内存块组织的,而是围绕任何大小的 I/O 片段组织的。
是的,在 Linux 中,存在“原始”块设备,可以在没有块缓存机制的情况下使用磁盘设备。但这个问题并没有被他们解决。
取而代之的是所谓的“日志文件系统”。对于日志文件系统,文件系统有机会指示内核哪些页面必须先于其他页面写出。如果文件系统中没有日志机制,那么它只会将块写入磁盘(更准确地说:写入块缓存),并且如果内核认为最佳,则它实际上会执行真正的写入操作。
您可以将日志文件系统想象成每个写入操作都会发生两次:首先写入“日志”(磁盘上的保留区域),然后才写入其真实位置。如果发生系统崩溃或磁盘错误,磁盘最后未损坏状态的内容可以非常快速且轻松地在日志上重建。
但这会显着降低写入性能,因为每次写入都必须完成两次。这就是为什么在现实中日志文件系统以更复杂的方式工作,它们使用各种复杂的数据结构操作来将这种开销减少到几乎看不见的水平。但这很难:例如,ext3 相对于 ext2 的主要改进是包含日志功能,这使其代码大小成倍增加。
在Linux中,块层API具有“屏障”机制。文件系统可以在其写操作之间设置“障碍”。障碍意味着数据后屏障将仅写入磁盘后每个数据前障碍已经被写出来了。日志文件系统使用屏障机制来指示块层有关实际写入操作所需的顺序。据我所知,他们不使用原始设备映射。
我不知道 FreeBSD 对此有何反应。也许他们消除块设备仅意味着一切都将与缓冲区缓存一起使用,而不是与块缓存一起使用。或者他们有一些东西,这里没有写。在文件系统内部,*BSD 和 Linux 世界之间存在很大差异。