QEMU虚拟机tap桥接网络设置

Written by: algebnaly

Date: 2026-05-13T14:26:51.000Z

简介

qemu的用户空间模拟网卡性能非常一般,和host通信iperf3只能跑出270MBit/s左右的性能。 这样的性能有时甚至会影响rdp远程的效果。因此有必要提升虚拟机的网络性能,当前最佳的方案是使用tap桥接网络,这种方式直接在内核态转发,避免了复制和上下文切换。测试时iperf能跑出30GBit/s的速度,百倍提升。

步骤

1. 使用libvirt创建桥接网卡

首先将libvirtd服务设置为开机自启动,省去日后手动创建网卡的麻烦。

sudo systemctl enable --now libvirtd

然后编辑默认网卡设置

<network xmlns:dnsmasq='http://libvirt.org/schemas/network/dnsmasq/1.0'>
  <name>default</name>
  <uuid>50e4730b-2e64-4eea-9da6-bb9f25410fdb</uuid>
  <forward mode='nat'/>
  <bridge name='virbr0' stp='on' delay='0'/>
  <mac address='52:54:00:31:0e:45'/>
  <dns enable='no'/>
  <ip address='192.168.122.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.122.2' end='192.168.122.254'/>
    </dhcp>
  </ip>
  <dnsmasq:options>
    <dnsmasq:option value='dhcp-option=6,8.8.8.8'/>
  </dnsmasq:options>
</network>

这里我故意将libvirt的dns功能关闭,防止和host上的dnsmasq服务冲突,但是还是通过dnsmasq namespace提供dnsmasq参数,让它提供虚拟机的DNS服务器参数。 然后就可以启动网卡了。

sudo virsh net-start default

接着,将这个网卡设置为自动启动,即如果libvirtd服务启动了的话,这个网卡也会被libvirtd服务启动。

 sudo virsh net-autostart default

如果网卡已经启动,修改网卡配置后需要先清理旧网卡配置,再重新启动网卡:

sudo virsh net-destroy default
sudo virsh net-start default

2. QEMU命令行参数

使用如下命令可以找到刚刚创建的桥接网卡:

ip link show type bridge

通常名字是virbr0,现在将这张网卡加入qemu的白名单

echo "allow virbr0" | sudo tee -a /etc/qemu/bridge.conf

在启动qemu之前,还需要手动创建tap设备,下面是命令行创建的方法:

sudo ip tuntap add dev tap_qemu mode tap user $(whoami)
sudo ip link set tap_qemu master virbr0
sudo ip link set tap_qemu up

我则是习惯通过python脚本完成tap设备的创建,启动虚拟机,以及虚拟机关闭后的清理。 具体的办法是在python脚本中创建如下几个函数:

def notify_error(msg: str):
    subprocess.run(f"notify-send 'run-win11' {msg!r} --icon=dialog-error", shell=True)

def cleanup_tap():
    subprocess.run(f"sudo ip link set {TAP_DEV} down", shell=True, stderr=subprocess.DEVNULL)
    subprocess.run(f"sudo ip tuntap del dev {TAP_DEV} mode tap", shell=True, stderr=subprocess.DEVNULL)

def setup_tap():
    r = subprocess.run(f"sudo ip tuntap add dev {TAP_DEV} mode tap user {USER}",
                       shell=True, capture_output=True, text=True)
    if r.returncode != 0:
        notify_error(f"Failed to create {TAP_DEV}: {r.stderr.strip()}")
        exit(1)

    atexit.register(cleanup_tap)
    signal.signal(signal.SIGTERM, lambda *_: exit(0))  # 触发 atexit
    signal.signal(signal.SIGINT,  lambda *_: exit(0))

    r = subprocess.run(f"sudo ip link set {TAP_DEV} master {BRIDGE}",
                       shell=True, capture_output=True, text=True)
    if r.returncode != 0:
        notify_error(f"Failed to attach {TAP_DEV} to {BRIDGE}: {r.stderr.strip()}")
        exit(1)

    r = subprocess.run(f"sudo ip link set {TAP_DEV} up",
                       shell=True, capture_output=True, text=True)
    if r.returncode != 0:
        notify_error(f"Failed to bring up {TAP_DEV}: {r.stderr.strip()}")
        exit(1)

setup_tap()
qemu-system-x86_64 \
  -netdev tap,id=net0,ifname={TAP_DEV},script=no,downscript=no \
  -device virtio-net-pci,netdev=net0 \
  ...

这样启动QEMU后,就能体验网卡性能飞跃了。