qemu 线程 vhost

vhost-net是Linux内核中的一个驱动模块,作为virtio-net的后端处理程序,将处理任务移到内核空间以提高网络性能。QEMU通过ioctl命令与vhost-net建立关系,利用eventfd进行guest与vhost之间的事件通信。vhost降低了数据传输中的CPU上下文切换和拷贝,提升了网络吞吐量和减少延迟。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[root@localhost cloud_images]# lsmod | grep vhost_net
vhost_net             262144  0 
vhost                 262144  1 vhost_net
tap                   262144  1 vhost_net
tun                   262144  2 vhost_net
[root@localhost cloud_images]#
vhost-net网卡的后端默认使用linux的虚拟网桥tap设备qemu和虚拟机内部使用virtio-net虚拟网卡。

步骤1: 创建linux网桥和tap设备(对于fedora,centos,redhat等默认有创建好的虚拟网卡)

brctl addbr virbr0

brctl stp virbr0 on

ip tuntap add name virbr0-nic mode tap

ip link set dev virbr0-nic up

步骤二:将host网卡添加到virbr0的一个port,并把ip配置给virbr0

brctl addif virbr0 eth0

brctl addif virbr0 virbr0-nic

dhclient virbr0

步骤三:用命令行起一个虚拟机

sudo x86_64-softmmu/qemu-system-x86_64 --enable-kvm -m 5120 -drive file=/home/fang/vm/centos.img,if=virtio -net nic,model=virtio -net tap,ifname=virbr0-nic,script=no -vnc :0

用vhost_net后端驱动

前面提到virtio在宿主机中的后端处理程序(backend)一般是由用户空间的QEMU提供的,然而如果对于网络IO请求的后端处理能够在在内核空间来完成,则效率会更高,会提高网络吞吐量和减少网络延迟。在比较新的内核中有一个叫做“vhost-net”的驱动模块,它是作为一个内核级别的后端处理程序,将virtio-net的后端处理任务放到内核空间中执行,从而提高效率

在第4章介绍“使用网桥模式”的网络配置时,有几个选项和virtio相关的,这里也介绍一下。

-net tap,[,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostforce=on|off]

vnet_hdr =on|off

设置是否打开TAP设备的“IFF_VNET_HDR”标识。“vnet_hdr=off”表示关闭这个标识;“vnet_hdr=on”则强制开启这个标识,如果没有这个标识的支持,则会触发错误。IFF_VNET_HDR是tun/tap的一个标识,打开它则允许发送或接受大数据包时仅仅做部分的校验和检查。打开这个标识,可以提高virtio_net驱动的吞吐量。

vhost=on|off

设置是否开启vhost-net这个内核空间的后端处理驱动,它只对使用MIS-X[5]中断方式的virtio客户机有效。

vhostforce=on|off

设置是否强制使用vhost作为非MSI-X中断方式的Virtio客户机的后端处理程序。

vhostfs=h

设置为去连接一个已经打开的vhost网络设备。

用如下的命令行启动一个客户机,就在客户机中使用virtio-net作为前端驱动程序,而后端处理程序则使用vhost-net(当然需要当前宿主机内核支持vhost-net模块)。

[root@jay-linux kvm_demo]# qemu-system-x86_64 rhel6u3.img -smp 2 -m 1024 -net nic,model=virtio,macaddr=00:16:3e:22:22:22 -net tap,vnet_hdr=on,vhost=on

VNC server running on ::1:5900'

qemu-system-aarch64: network script /usr/local/bin/../etc/qemu-ifup failed with status 256

[root@localhost cloud_images]# cat qemu-ifup 
#!/bin/sh
set -x

switch=virbr0 

if [ -n "$1" ];then
    ip tuntap add $1 mode tap user `whoami`
    ip link set $1 up
    sleep 0.5s
    ip link set $1 master $switch
    exit 0
else
    echo "Error: no interface specified"
    exit 1
fi
qemu-system-aarch64  -name vm2 -daemonize \
  -enable-kvm -M virt -cpu host -smp 2 -m 4096 \
  -object memory-backend-file,id=mem,size=4096M,mem-path=/mnt/huge,share=on \
  -numa node,memdev=mem -mem-prealloc \
  -global virtio-blk-device.scsi=off \
  -device virtio-scsi-device,id=scsi \
  -kernel vmlinuz-4.18 --append "console=ttyAMA0  root=UUID=6a09973e-e8fd-4a6d-a8c0-1deb9556f477 iommu=pt intel_iommu=on iommu.passthrough=1" \
  -initrd initramfs-4.18 \
 -drive file=vhuser-test1.qcow2  \
 -serial telnet:localhost:4322,server,nowait \
 -monitor telnet:localhost:4321,server,nowait \
 -net nic,model=virtio,macaddr=00:16:3e:22:22:22 -net tap,id=hostnet1,script=qemu-ifup,vnet_hdr=on,vhost=on \
 -vnc :10

当给一个Qemu进程传递了参数-netdev tap,vhost=on 的时候,QEMU会通过调用几个ioctl命令对这个文件描述符进行一些初始化的工作,然后进行特性的协商,从而宿主机跟客户机的vhost-net driver建立关系。 QEMU代码调用如下:

vhost_net_init -> vhost_dev_init -> vhost_net_ack_features

+ switch=virbr0
+ '[' -n tap0 ']'
++ whoami
+ ip tuntap add tap0 mode tap user root
ioctl(TUNSETIFF): Device or resource busy
+ ip link set tap0 up
+ sleep 0.5s
+ ip link set tap0 master virbr0
+ exit 0

[root@localhost cloud_images]# brctl show
bridge name     bridge id               STP enabled     interfaces
virbr0          8000.5254000d3f71       yes             enp125s0f1
                                                        tap0
                                                        virbr0-nic

[root@localhost cloud_images]# ps -elf | grep vhost
7 S root      49044      1  9  80   0 - 88035 poll_s 21:56 ?        00:00:11 qemu-system-aarch64 -name vm2 -daemonize -enable-kvm -M virt -cpu host -smp 2 -m 4096 -object memory-backend-file,id=mem,size=4096M,mem-path=/mnt/huge,share=on -numa node,memdev=mem -mem-prealloc -global virtio-blk-device.scsi=off -device virtio-scsi-device,id=scsi -kernel vmlinuz-4.18 --append console=ttyAMA0  root=UUID=6a09973e-e8fd-4a6d-a8c0-1deb9556f477 iommu=pt intel_iommu=on iommu.passthrough=1 -initrd initramfs-4.18 -drive file=vhuser-test1.qcow2 -serial telnet:localhost:4322,server,nowait -monitor telnet:localhost:4321,server,nowait -net nic,model=virtio,macaddr=00:16:3e:22:22:22 -net tap,id=hostnet1,script=qemu-ifup,vnet_hdr=on,vhost=on -vnc :10
1 S root      49065      2  0  80   0 -     0 vhost_ 21:56 ?        00:00:00 [vhost-49044]

[root@localhost ~]# cat /proc/49065/stack 
[<ffff000008085ed4>] __switch_to+0x8c/0xa8
[<ffff000001e507d8>] vhost_worker+0x148/0x170 [vhost]
[<ffff0000080f8638>] kthread+0x10c/0x138
[<ffff000008084f54>] ret_from_fork+0x10/0x18
[<ffffffffffffffff>] 0xffffffffffffffff
[root@localhost ~]# 

### vhost内核线程方式与其他方式的区别与比较 #### 1. **vhost内核线程方式** vhost内核线程方式通过Linux内核中的`vhost-net`模块来加速虚拟网络设备的数据包处理过程。这种方式利用了内核空间的优势,减少了用户态和内核态之间的上下文切换开销。具体来说: - 数据路径被优化到内核中执行,从而减少QEMU的参与程度[^1]。 - 使用`ioctl`接口与用户空间程序(如QEMU)进行交互,简化了控制面的操作[^5]。 这种设计使得性能显著提升,尤其是在高吞吐量场景下表现优异。然而,由于依赖于特定版本的内核支持,其灵活性受到一定限制。 #### 2. **其他方式:纯软件模拟与vhost-user** ##### (a) 纯软件模拟 纯软件模拟是指完全由QEMU或其他用户态进程完成I/O操作的方式。在这种模式下: - 需要频繁地在用户态和内核态之间切换以捕获并响应硬件中断或访问请求[^4]。 - 性能较低,尤其当面对大量小型数据包时会显得效率低下。 尽管如此,在某些特殊情况下(比如老旧操作系统或者缺乏适当驱动的支持),它仍然是一种可行的选择。 ##### (b) vhost-user 作为另一种替代方案,vhost-user允许将vhost逻辑迁移到独立的应用层服务进程中运行,而不是固定绑定至本地机器上的某个特定内核实例之上: - 提供更高的隔离性和可移植性,因为前端可以位于远程服务器甚至不同的物理位置; - 更容易集成进容器化环境中去构建更加灵活的服务架构; 不过与此同时也会引入额外延迟成本以及复杂度增加等问题需要注意解决. #### 3. **对比总结表** | 特性 | vhost 内核线程 | 纯软件模拟 | vhost-user | |-------------------|--------------------|---------------------|--------------------| | 执行环境 | Linux 内核 | 用户态 | 用户态 | | 上下文切换频率 | 较少 | 多 | 少 | | 控制平面通信方法 | ioctl 接口 | API 调用 | RPC 协议 | | 数据平面性能 | 高 | 中/低 | 高 | | 移动性和跨平台能力 | 差 | 极差 | 好 | 综上所述,每种技术都有各自适用范围及优缺点,在实际部署过程中应根据需求权衡选用最合适的解决方案。 ```python # 示例代码展示如何判断当前使用的是否为vhost-net机制 def is_vhost_net_used(): try: with open('/proc/modules', 'r') as f: modules = f.read() if 'vhost_net' in modules: return True else: return False except Exception as e: print(f"Error occurred: {e}") return None if __name__ == "__main__": result = is_vhost_net_used() if result is not None: print("Vhost-net module is loaded." if result else "Vhost-net module isn't loaded.") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值