块设备提供缓冲。这意味着write()
在内核将数据写入设备之前,块设备上可以返回成功。程序可以通过调用 来等待所有缓冲的写入fsync()
。
我已使用dd
(或cat
) 将文件系统映像写入设备。默认情况下不调用这些命令fsync()
。
接下来,假设我想将mount
写入的块设备作为文件系统。
我认为最安全的做法是sync
在安装之前使用该命令。但是如果我不同步块设备怎么办?文件系统是否可能尝试读取一些尚未写入设备的块?那么它是否可以读取设备的旧内容,而不是文件系统映像中的正确数据?
我的主要兴趣是 Linux 行为。 (StackExchange 鼓励我提出一个具体问题。不过,我也可以对任何替代或历史行为投赞成票:-)。
答案1
当程序关闭块设备文件时,Linux 会刷新关联的缓存,迫使程序等待。但这仅适用于最后一个close()
。如果其他东西仍然打开块设备,则不会发生这种情况。包括如果任意分区同一块设备的仍处于打开状态。
因此,在一般情况下,最好以某种方式同步设备。
为了安全起见,同步设备的方式是dd
使用选项运行命令conv=fsync
。如果没有这个,内核将不会返回写入错误。因此,如果您查看内核日志 ( dmesg
),您只会注意到错误。
除了等待所有缓存的写入之外,最后一个close()
还会删除所有缓存 ( kill_bdev()
)。我已经通过观察命令的输出亲自验证了这一点free
。
linux-4.20/fs/block_dev.c:1778
static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part)
{
struct gendisk *disk = bdev->bd_disk;
struct block_device *victim = NULL;
mutex_lock_nested(&bdev->bd_mutex, for_part);
if (for_part)
bdev->bd_part_count--;
if (!--bdev->bd_openers) {
WARN_ON_ONCE(bdev->bd_holders);
sync_blockdev(bdev);
kill_bdev(bdev);
如果您不熟悉 C 代码,上面的最后一个块相当于:
bdev->bd_openers = bdev->bd_openers - 1;
if (bdev->bd_openers == 0) {
WARN_ON_ONCE(bdev->bd_holders);
sync_blockdev(bdev);
kill_bdev(bdev);
答案2
您只需要sync
/fsync
即可正确处理系统崩溃。
内核为您提供一致性。任何写入操作都将位于脏缓冲区中,直到刷新到磁盘,并且任何读取操作都将首先从这些脏缓冲区中得到应答。
但是,如果在同步之前断电,内核缓冲区就会消失,并且 dd(1) 的一部分将不会被保留。这可能会很尴尬。
除了崩溃之外,您基本上可以忘记缓冲,因为内核会透明地处理这一切。