Linux页面缓存一致性

Linux页面缓存一致性

我有几个关于 Linux 页面缓存的一般性问题。据我了解,(至少)有三种方法可以将块设备上的块与页面缓存中的块缓冲区相关联:

  1. 通过磁盘设备文件,例如/dev/sda
  2. 通过分区设备文件,例如,/dev/sda1
  3. 通过常规文件,例如/home/me/hello

假设/dev/sda1包含一个典型的 Linux 文件系统,例如 ext2,它安装在该文件系统上/并且/home文件夹位于该文件系统中。

这三个方法具有不同的页面缓存,因为它们的address_space对象嵌入在不同的inode对象中:

  1. bdev特殊文件系统inode,用于/dev/sda
  2. bdev特殊文件系统inode,用于/dev/sda1
  3. ext2文件系统的inode, 对于/home/me/hello

现在问题如下:

  1. 我认为super_block对象只是为已安装的文件系统分配的,并且由于bdev没有真正安装在任何地方,因此没有super_block对象。因此,当writeback_inodes()循环对象super_block并搜索 dirty时inode,上述方法 1 和 2 中的页面缓存将会丢失,因此它们需要手动同步到磁盘?

  2. 是否有可能用户可以通过上面列出的所有三种方法来读/写磁盘上属于文件的块/home/me/hello,从而使三个页面缓存中的内容不同步?我可以在内核 2.6.11 代码中看到在方法 3 中写入文件时需要付出努力,它等待设备缓存同步然后继续,尽管该设备可以是磁盘设备(方法 1)或分区设备(方法 2) ),但不可能两者兼而有之。而且我在内核5.3中没有找到类似的代码:

等待设备缓存的代码:

static int __block_prepare_write(struct inode *inode, struct page *page,
        unsigned from, unsigned to, get_block_t *get_block)
{
    ...
    unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr);
    ...
}

void unmap_underlying_metadata(struct block_device *bdev, sector_t block)
{
    ...
    old_bh = __find_get_block_slow(bdev, block, 0);
    if (old_bh) {
        clear_buffer_dirty(old_bh);
        wait_on_buffer(old_bh);
        clear_buffer_req(old_bh);
        __brelse(old_bh);
    }
}

对于 Linux 内核来说还是个新手,因此这些问题可能没有多大意义......指针赞赏!

答案1

将文件系统上的文件的写入(或读取)与文件系统所在的块设备的写入混合在一起是不正常的。后者仅用于创建分区或在分区上创建文件系统等任务。

创建并安装文件系统时,写入块设备可能会导致缓存不同步,但这并不是唯一的问题:绕过文件系统并直接写入块设备肯定会损坏文件系统。

写入块设备是只有 root 才能执行的操作。 Linux 提供了很多让 root 搬起石头砸自己脚的方法,并且假设您可以相信 root 不会这样做。

答案2

Q1 [...] 由于bdev没有真正安装在任何地方,因此没有super_block对象。因此,当writeback_inodes()循环 super_block 对象并搜索 dirty时inode,上述方法 1 和 2 中的页面缓存将被丢失 [...]

linux/v5.3/source/fs/block_dev.c:841

struct super_block *blockdev_superblock __read_mostly;
EXPORT_SYMBOL_GPL(blockdev_superblock);

void __init bdev_cache_init(void)
{
    int err;
    static struct vfsmount *bd_mnt;

    bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode),
            0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
                SLAB_MEM_SPREAD|SLAB_ACCOUNT|SLAB_PANIC),
            init_once);
    err = register_filesystem(&bd_type);
    if (err)
        panic("Cannot register bdev pseudo-fs");
    bd_mnt = kern_mount(&bd_type);
    if (IS_ERR(bd_mnt))
        panic("Cannot create bdev pseudo-fs");
    blockdev_superblock = bd_mnt->mnt_sb;   /* For writeback */
}

相关内容