AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 753092
Accepted
Binarus
Binarus
Asked: 2023-08-03 06:13:13 +0800 CST2023-08-03 06:13:13 +0800 CST 2023-08-03 06:13:13 +0800 CST

这两个 blockdev 声明有什么区别?

  • 772

在 Debian bullseye 中,我通过命令行使用 QEMU / KVM 启动虚拟机(即,没有virsh或其他帮助器 / 包装器)。其中一个虚拟机从声明如下的块设备启动:

-blockdev driver=file,node-name=q1,filename=/dev/loop0 \

就在今天,我无意中注意到 QEMU 在启动该 VM 时发出以下警告:

Opening a block device as a file using the 'file' driver is deprecated

一些研究表明这个警告是已知的,并且有解决方案,例如@Stephen Kitt在这里接受的答案提出了以下blockdev声明:

-blockdev node-name=q1,driver=raw,file.driver=host_device,file.filename=/dev/loop0 \

这个解决方案无疑是有效的,但我找不到任何有关file.driver=host_device. 因此,我测试了一些其他选项,并得出了以下似乎也有效的解决方案:

-blockdev driver=host_device,node-name=q1,filename=/dev/loop0 \

有人可以简单解释一下这两个声明之间的区别吗?值得注意的是,其中一个在延迟或吞吐量方面是否会优于另一个?

作为一个额外的问题,有人知道文档在哪里吗host_device?在上面链接的其他问题/答案中,有一个指向可能实现该驱动程序的提交的链接。但是,我也找不到该链接背后的任何文档。

qemu
  • 1 1 个回答
  • 21 Views

1 个回答

  • Voted
  1. Best Answer
    telcoM
    2023-08-03T16:16:18+08:002023-08-03T16:16:18+08:00

    根据QEMU 变更日志,在 QEMU 版本 3.0 中,访问主机块设备时驱动程序被弃用file并引入了驱动程序。host_device

    您提到的 Stephen Kitt 的答案中的 PDF 演示文稿(请参阅第 20...24 页)表明,定义 QEMU 存储的最详尽的方法意味着首先使用 a 设置数据位置(file或host_device)node_name=,然后使用另一个--blockdev选项file=<previously_defined_node_name>在第一层之上添加另一层以指定数据格式(例如raw或qcow2例如)。这为用户提供了极好的控制,但对于大多数基本情况来说就太过分了。

    在 QEMU 源代码存储库中,该qemu-options.hx文件似乎具有-blockdev迄今为止我所见过的选项的最佳描述。

    有一段话或许可以为我们解开这个file.<something>=<something>谜团提供一些启示:

    期望引用另一个节点(例如file)的选项可以通过两种方式给出。您可以指定已存在节点的节点名称 ( file=node-name),也可以定义内联新节点,在点 ( file.filename=path,file.aio=native) 后添加引用节点的选项。

    因此,file.<something>=语法本质上是指定另一个-blockdev声明的简写方式,同时也避免了“中间节点”名称使命名空间变得混乱node_name。

    所以,史蒂芬·基特的blockdev声明:

    -blockdev node-name=q1,driver=raw,file.driver=host_device,file.filename=/dev/loop0
    

    似乎相当于扩展形式:

    -blockdev node_name=<hidden_node>,driver=host_device,filename=/dev/loop0 \
    -blockdev node_name=q1,driver=raw,file=<hidden_node>
    

    这和你的声明之间的区别

    -blockdev driver=host_device,node-name=q1,filename=/dev/loop0 
    

    这是一个比较棘手的问题,需要深入研究 QEMU 源代码才能弄清楚。

    QEMU的-blockdev驱动程序file,host_device并且host_cdrom针对不同的主机架构有多个版本。对于 Linux,适用的位于block/file-posix.c. 搜索字符串的实例BlockDriver bdrv_,您将找到每一个。(您还会发现,host_cdrom出于某种原因,FreeBSD 有一个完全独立的定义。)

    每个BlockDriver似乎都是由(大部分)函数指针的结构定义的。这些指针可能指向特定于驱动程序的函数,或者引用与另一个驱动程序共享的公共实现。

    驱动file程序定义如下:

    BlockDriver bdrv_file = {
        .format_name = "file",
        .protocol_name = "file",
        .instance_size = sizeof(BDRVRawState),
        .bdrv_needs_filename = true,
        .bdrv_probe = NULL, /* no probe for protocols */
        .bdrv_parse_filename = raw_parse_filename,
        .bdrv_file_open = raw_open,
        .bdrv_reopen_prepare = raw_reopen_prepare,
        .bdrv_reopen_commit = raw_reopen_commit,
        .bdrv_reopen_abort = raw_reopen_abort,
        .bdrv_close = raw_close,
        .bdrv_co_create = raw_co_create,
        .bdrv_co_create_opts = raw_co_create_opts,
        .bdrv_has_zero_init = bdrv_has_zero_init_1,
        .bdrv_co_block_status = raw_co_block_status,
        .bdrv_co_invalidate_cache = raw_co_invalidate_cache,
        .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes,
        .bdrv_co_delete_file = raw_co_delete_file,
    
        .bdrv_co_preadv         = raw_co_preadv,
        .bdrv_co_pwritev        = raw_co_pwritev,
        .bdrv_co_flush_to_disk  = raw_co_flush_to_disk,
        .bdrv_co_pdiscard       = raw_co_pdiscard,
        .bdrv_co_copy_range_from = raw_co_copy_range_from,
        .bdrv_co_copy_range_to  = raw_co_copy_range_to,
        .bdrv_refresh_limits = raw_refresh_limits,
        .bdrv_attach_aio_context = raw_aio_attach_aio_context,
    
        .bdrv_co_truncate                   = raw_co_truncate,
        .bdrv_co_getlength                  = raw_co_getlength,
        .bdrv_co_get_info                   = raw_co_get_info,
        .bdrv_get_specific_info             = raw_get_specific_info,
        .bdrv_co_get_allocated_file_size    = raw_co_get_allocated_file_size,
        .bdrv_get_specific_stats = raw_get_specific_stats,
        .bdrv_check_perm = raw_check_perm,
        .bdrv_set_perm   = raw_set_perm,
        .bdrv_abort_perm_update = raw_abort_perm_update,
        .create_opts = &raw_create_opts,
        .mutable_opts = mutable_opts,
    };
    

    所以它本质上是驱动程序的克隆raw,几乎所有功能都引用它。

    定义host_device稍微复杂一点:

    static BlockDriver bdrv_host_device = {
        .format_name        = "host_device",
        .protocol_name        = "host_device",
        .instance_size      = sizeof(BDRVRawState),
        .bdrv_needs_filename = true,
        .bdrv_probe_device  = hdev_probe_device,
        .bdrv_parse_filename = hdev_parse_filename,
        .bdrv_file_open     = hdev_open,
        .bdrv_close         = raw_close,
        .bdrv_reopen_prepare = raw_reopen_prepare,
        .bdrv_reopen_commit  = raw_reopen_commit,
        .bdrv_reopen_abort   = raw_reopen_abort,
        .bdrv_co_create_opts = bdrv_co_create_opts_simple,
        .create_opts         = &bdrv_create_opts_simple,
        .mutable_opts        = mutable_opts,
        .bdrv_co_invalidate_cache = raw_co_invalidate_cache,
        .bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes,
    
        .bdrv_co_preadv         = raw_co_preadv,
        .bdrv_co_pwritev        = raw_co_pwritev,
        .bdrv_co_flush_to_disk  = raw_co_flush_to_disk,
        .bdrv_co_pdiscard       = hdev_co_pdiscard,
        .bdrv_co_copy_range_from = raw_co_copy_range_from,
        .bdrv_co_copy_range_to  = raw_co_copy_range_to,
        .bdrv_refresh_limits = raw_refresh_limits,
        .bdrv_attach_aio_context = raw_aio_attach_aio_context,
    
        .bdrv_co_truncate                   = raw_co_truncate,
        .bdrv_co_getlength                  = raw_co_getlength,
        .bdrv_co_get_info                   = raw_co_get_info,
        .bdrv_get_specific_info             = raw_get_specific_info,
        .bdrv_co_get_allocated_file_size    = raw_co_get_allocated_file_size,
        .bdrv_get_specific_stats = hdev_get_specific_stats,
        .bdrv_check_perm = raw_check_perm,
        .bdrv_set_perm   = raw_set_perm,
        .bdrv_abort_perm_update = raw_abort_perm_update,
        .bdrv_probe_blocksizes = hdev_probe_blocksizes,
        .bdrv_probe_geometry = hdev_probe_geometry,
    
        /* generic scsi device */
    #ifdef __linux__
        .bdrv_co_ioctl          = hdev_co_ioctl,
    #endif
    
        /* zoned device */
    #if defined(CONFIG_BLKZONED)
        /* zone management operations */
        .bdrv_co_zone_report = raw_co_zone_report,
        .bdrv_co_zone_mgmt = raw_co_zone_mgmt,
        .bdrv_co_zone_append = raw_co_zone_append,
    #endif
    };
    

    它还引用raw驱动程序的大部分功能,但有自己的专用功能,例如:

    • 探测设备
    • 解析文件名(大概是为了检查有效的主机设备)
    • 打开设备(如果您深入挖掘,您会发现以 MacOS 上的用户身份访问块设备需要相当多的额外代码,因此这很可能是特定于操作系统的事情所需要的)
    • 快速写入大块零
    • 丢弃数据
    • 检查和设置设备权限
    • 获取设备统计信息
    • 检测设备的块大小和驱动器几何形状
    • 传递通用 SCSI 命令

    其中一些host_device特定函数只是执行额外的错误检查,然后调用驱动程序的等效函数raw。但它可能是最后一个提供了驱动程序的大部分价值host_device:它允许虚拟机显式地查看主机设备的实际块大小和几何结构(如果适用)。通过通用 SCSI 命令,VM 可以被授予访问磁带库机器人、大容量磁带驱动器和 DVD 刻录机等内容的权限......以及许多其他内容。

    您的较短声明有效,因为在大多数情况下host_device声明本质上都会回落到驱动程序的功能raw。

    请注意,上述内容仅适用于 POSIX 风格的操作系统:如果您在 Windows 上运行 QEMU,设备访问可能必须与常规文件访问完全不同,并且您的较短声明可能根本不起作用。file和之间的区别可能host_device主要是为了将 QEMU 移植到其他系统架构,其中设备访问与常规文件有很大不同。

    您担心性能,但我希望性能基本相同,因为所有三个驱动程序最终都会调用完全相同的代码段来实现核心功能、bdrv_co_preadv和bdrv_co_pwritev函数。

    当将驱动程序彼此分层时,可以非常简单地检测给定驱动程序是否使用与将在其之上分层的驱动程序完全相同的功能,并优化消除重复。我的第一个猜测是,这样的优化实际上是非常必要的,以避免各种愚蠢的行为。因此,如果配置最终执行完全相同的操作,我希望它执行相同的操作,无论它是如何声明的。

    • 1

相关问题

  • KVM:无法销毁 VM(权限被拒绝) - AppArmor 阻止 Libvirt

  • QEMU-KVM 来宾 rootfs 只读问题

  • QEMU 4个以上的串口

  • 在 MIPS 上运行为 IRIX 制作的旧程序的方法?

  • 为什么 KVM/QEMU 中的 vm 称为域?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve