QEMU 设备模拟逻辑

概述

参考:

QEMU 模拟设备主要是半虚拟化设备,从这里可以看到简单的介绍。

QEMU Storage Emulation(QEMU 存储模拟)

网络模拟 类似,QEMU 想要让虚拟机获得一块硬盘,也需要由两部分组成一个完整的存储功能。

  1. front-end(前端) # VM 中的 块设备
  2. back-end(后端) # 宿主机中的与 VM 中模拟出来的块设备进行交互的设备。

QEMU Network Emulation(QEMU 网络模拟)

参考:

QEMU 想要让虚拟机与外界互通,需要由两部分组成一个完整的网络功能:

  • front-end(前端) # VM 中的 NIC(Network Interface Controller,即人们常说的网卡)。
    • VM 中的 NIC 是由 QEMU 模拟出来的,在支持 PCI 卡的系统上,通常可以是 e1000 网卡、rtl8139 网卡、virtio-net 设备。
  • back-end(后端) # 宿主机中的与 VM 中模拟出来的 NIC 进行交互的设备。
    • back-end 有多种类型可以使用,这些后端可以用于将 VM 连接到真实网络,或连接到另一个 VM

效果如图所示:

基本应用示例

在使用 qemu-kvm 命令创建虚拟机时,通过一组两个选项来为虚拟机创建一个网络设备。比如:

  • -netdev tap,id=n1 # 在宿主机创建一个后端设备,这是一个 tap 类型的网络设备(tap 类型的设备路径为 /dev/net/tun)
  • -device virtio-net-pci,netdev=n1 # 在 VM 中模拟一个前端设备,这是一个 virtio-net-pci 类型的网卡

完整的命令如下:

qemu-kvm -m 4096 -smp 2 -name test \
-drive file=/var/lib/libvirt/images/test-1.bj-net.qcow2,format=qcow2,if=virtio \
-netdev tap,id=n1 \
-device virtio-net-pci,netdev=n1 \
-vnc :3

此时 qemu-kvm 发现 后端设备的 id前端设备的属性(netdev)的值 一致,那么 qemu-kvm 就会将 两端设备关联。因此,在 VM 启动时,其打开了设备文件 /dev/net/tun 并获得了读写该文件的文件描述符 (FD)XX,同时向内核注册了一个 tap 类型虚拟网卡 tapX,tapX 与 FD XX 关联,虚拟机关闭时 tapX 设备会被内核释放。此虚拟网卡 tapX 一端连接用户空间程序 qemu-kvm,另一端连接主机链路层。

# 通过进程,获取该进程所使用的网络设备
# 当先宿主机上有3个虚拟机,分别对应 82649、82747、144776 这三个进程
# 82649 与 82747 使用网卡多队列功能,启用了8个队列,144776 仅有一个网卡队列
# 所以,82649 与 82747 打开了8个 /dev/net/tun 设备,而 144776 打开了1个 /dev/net/tun 设备
[root@host-3 fdinfo]# lsof /dev/net/tun
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
qemu-kvm  82649 qemu   27u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82649 qemu   29u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82649 qemu   31u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82649 qemu   32u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82649 qemu   33u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82649 qemu   34u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82649 qemu   35u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82649 qemu   36u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82747 qemu   28u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82747 qemu   31u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82747 qemu   32u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82747 qemu   33u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82747 qemu   34u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82747 qemu   35u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82747 qemu   36u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm  82747 qemu   37u   CHR 10,200      0t0 102414 /dev/net/tun
qemu-kvm 144776 qemu   31u   CHR 10,200      0t0 102414 /dev/net/tun
# 查看 144776 进程的 fdinfo 中的 31 号描述符文件,可以看到该进程关联的网络设备是 vnet2
[root@host-3 fdinfo]# cat /proc/144776/fdinfo/31
pos: 0
flags: 0104002
mnt_id: 20
iff: vnet2

## 通过网络设备,获取使用该设备的进程
# 可以通过一条命令来直接获取使用指定 pid 设备的进程
# 下面的命令可以获取使用 vnet2 这个 tap 类型的网络设备的进程。
[root@host-3 fdinfo]# egrep -l iff:.*vnet2 /proc/*/fdinfo/* 2> /dev/null | cut -d/ -f3
144776

所以 144776 这个进程下的虚拟机经过其内部网卡发送的数据包,都会发送到 /dev/net/tun 设备上,然后根据其文件描述符,找到对应的 tap 设备,将数据包转发过去。这样,虚拟机的数据就通过网络,发送到物理机上了。

获取 tap 设备 与 VM 关联性的方法参考:https://unix.stackexchange.com/questions/462171/how-to-find-the-connection-between-tap-interface-and-its-file-descriptor

virbr0 说明

virbr0 是 KVM 默认创建的一个 Bridge,其作用是为连接其上的虚机网卡提供 NAT 访问外网的功能。virbr0 默认分配了一个 IP 192.168.122.1,并为连接其上的其他虚拟网卡提供 DHCP 服务。

  • 需要说明的是,使用 NAT 的虚机 VM1 可以访问外网,但外网无法直接访问 VM1。 因为 VM1 发出的网络包源地址并不是 192.168.122.6,而是被 NAT 替换为宿主机的 IP 了。
  • 这个与使用 br0 不一样,在 br0 的情况下,VM1 通过自己的 IP 直接与外网通信,不会经过 NAT 地址转换。

最后修改 May 26, 2024: browser extensions, clearup (445d6e1f)