web-dev-qa-db-ja.com

ブロックデバイスキャッシュv.s.ファイルシステム

ブロックデバイスはバッファリングを提供します 。これは、カーネルがデータをデバイスに書き込む前に、ブロックデバイスのwrite()が成功を返すことができることを意味します。プログラムは、fsync()を呼び出すことにより、バッファリングされたすべての書き込みを待機できます。

dd(またはcat)を使用して、ファイルシステムイメージをデバイスに書き込みました。これらのコマンドは、デフォルトではfsync()を呼び出しません。

次に、書き込まれたブロックデバイスをファイルシステムとしてmountしたいとします。

私はそれが例えばするのが最も安全だと思います。マウントする前にsyncコマンドを使用してください。しかし、ブロックデバイスを同期しないとどうなりますか?ファイルシステムが、デバイスにまだ書き込まれていないいくつかのブロックを読み取ろうとする可能性はありますか?次に、ファイルシステムイメージから正しいデータではなく、デバイスの古いコンテンツを読み取ることができますか?

私の主な関心はLinuxの振る舞いです。 (そしてStackExchangeは私に1つの特定の質問をすることを勧めます。私はどんな代替的または歴史的な振る舞いにも賛成することができます:-)。

2
sourcejedi

プログラムがブロックデバイスファイルを閉じると、Linuxは関連するキャッシュをフラッシュし、プログラムを強制的に待機させます。ただし、これは最後のclose()にのみ適用されます。他の何かがまだブロックデバイスを開いている場合、それは起こりません。同じブロックデバイスの任意のパーティションがまだ開いているかどうかを含みます。

したがって、一般的なケースでは、何らかの方法でデバイスを同期するのが最善です。

安全のために、デバイスを同期する方法は、オプション_conv=fsync_を使用してddコマンドを実行することです。これがないと、カーネルは書き込みエラーを返しません。したがって、カーネルログ(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
sourcejedi