块设备缓存与文件系统

块设备缓存与文件系统

块设备提供缓冲。这意味着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) 的一部分将不会被保留。这可能会很尴尬。

除了崩溃之外,您基本上可以忘记缓冲,因为内核会透明地处理这一切。

相关内容