在 Linux 下使用 QEMU/KVM 优雅运行与自动化测试 Windows 虚拟机

在跨平台底层开发(如网络透明代理、Wintun 虚拟网卡、文件系统或驱动开发)过程中,常常需要在 Linux 宿主机上对 Windows 平台做持续构建、测试与端到端验证。

传统的桌面虚拟化工具(如 VMware Workstation、VirtualBox)存在以下痛点:

  1. 开销过大:默认 Windows 10/11 开机动辄占用 3.5GB+ 内存,后台大量的遥测与 Defender 扫描吃满 CPU 与磁盘 I/O。
  2. 难以自动化:依赖 GUI 手动点击,难以与 Linux 终端开发流或 CI/CD 脚本无缝串联。
  3. 环境污染:测试过程可能会修改系统注册表、驱动、路由表,一旦崩溃需要反复手动快照恢复。

本文总结了一套极简、轻量、无感远程控制的工程实践方案。


一、镜像选型:避免深坑,拥抱轻量

在测试底层系统/网络功能时,切勿盲目选用社区过度阉割版(如 Tiny10/Tiny11),它们常常移除了必要的数字证书链、NDIS 驱动子系统或 Winsock 高级 API,导致难以排查究竟是代码 Bug 还是系统被剪坏。


二、QEMU/KVM 极简启动与安装配置

使用 libvirtvirt-install 工具快速初始化一台开箱即用的测试虚拟机。

1. 准备工作

下载官方镜像以及 Red Hat 官方发布的 Windows VirtIO 驱动光盘:

# VirtIO 官方驱动光盘(包含网卡、SCSI 磁盘控制器、Balloon 内存气球驱动和 Guest Agent)
wget https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso

2. 一键创建虚拟机配置

重点配置:

virt-install \
  --connect qemu:///system \
  --name win10-ltsc \
  --memory 4096 \
  --vcpus 4 \
  --os-variant win10 \
  --cdrom /path/to/en-us_windows_10_iot_enterprise_ltsc_2021_x64_dvd.iso \
  --disk /path/to/virtio-win.iso,device=cdrom,bus=sata \
  --disk size=25,format=qcow2,bus=virtio \
  --network network=default,model=virtio \
  --graphics spice,listen=127.0.0.1 \
  --channel spicevmc,target_type=virtio,name=com.redhat.spice.0 \
  --channel unix,target_type=virtio,name=org.qemu.guest_agent.0 \
  --noautoconsole

3. 安装过程加载 VirtIO 磁盘驱动

在 Windows 安装界面的“选择安装驱动器”步骤中,因使用了 VirtIO 总线,需手动加载驱动:

  1. 点击 Load driver -> Browse
  2. 选中挂载的光驱:virtio-win -> viostor -> w10 -> amd64
  3. 点击确定,即可识别出 25GB 的 VirtIO 虚拟硬盘并继续安装。

三、系统内装机后优化:极致减负与激活

进入桌面后,打开光驱 virtio-win

  1. 进入 guest-agent 目录,双击安装 qemu-ga-x86_64.msi(启动宿主机控制通道)。
  2. 安装完成后,即可彻底关闭图形窗口,剩下的所有优化全部由宿主机在终端一条命令完成!

1. 永久激活系统(HWID 数字权利)

通过开源标准激活工具执行数字权利永久授权:

irm https://get.activated.win | iex

(选择 1 即可一键完成数字许可证永久激活)

2. 榨干系统性能:禁用 Defender 扫描与耗能后台服务

Windows Defender 对本地编译、写日志和频繁创建进程有明显的锁与扫描开销。通过系统管理命令彻底关闭非必要的后台服务:

# 1. 关闭 Defender 实时监控与样本上报 (提高 I/O 与进程创建效率 30% 以上)
Set-MpPreference -DisableRealtimeMonitoring $true -ErrorAction SilentlyContinue
Set-MpPreference -DisableBehaviorMonitoring $true -ErrorAction SilentlyContinue
Set-MpPreference -DisableBlockAtFirstSeen $true -ErrorAction SilentlyContinue
Set-MpPreference -DisableIOAVProtection $true -ErrorAction SilentlyContinue
Set-MpPreference -DisableScriptScanning $true -ErrorAction SilentlyContinue
Set-MpPreference -SubmitSamplesConsent 2 -ErrorAction SilentlyContinue

# 2. 停用并禁用无用的后台耗能服务
$services = @("wuauserv", "WSearch", "SysMain", "DiagTrack", "Spooler", "MapsBroker")
foreach ($s in $services) {
    Stop-Service -Name $s -Force -ErrorAction SilentlyContinue
    Set-Service -Name $s -StartupType Disabled -ErrorAction SilentlyContinue
}

经过优化后,虚拟机日常开机空闲内存维持在 1.36 GB,CPU 占用率稳定在 0%。


四、全自动化控制利器:基于 QEMU Guest Agent 的无感脚本执行

在 Linux 下测试 Windows 程序,无需配置繁琐的 SSH 服务或暴露 RDP 端口。通过 QEMU 的内置通道,可以直接下发 PowerShell 指令并取回输出。

编写宿主机控制脚本 scripts/win_exec.py

#!/usr/bin/env python3
import subprocess
import json
import base64
import time
import sys
import os

DEFAULT_VM = os.environ.get("VM", "win10-ltsc")

def qemu_agent(cmd_dict, vm=None):
    if vm is None:
        vm = DEFAULT_VM
    cmd = ["virsh", "-c", "qemu:///system", "qemu-agent-command", vm, json.dumps(cmd_dict)]
    p = subprocess.run(cmd, capture_output=True, text=True)
    if p.returncode != 0:
        raise RuntimeError(f"virsh error: {p.stderr.strip()}")
    return json.loads(p.stdout)

def run_powershell(ps_command, timeout=30):
    # 使用 UTF-16LE + Base64 编码,避免特殊字符转义错误
    encoded = base64.b64encode(ps_command.encode('utf-16le')).decode('ascii')
    payload = {
        "execute": "guest-exec",
        "arguments": {
            "path": "powershell.exe",
            "arg": ["-NoProfile", "-NonInteractive", "-EncodedCommand", encoded],
            "capture-output": True
        }
    }
    res = qemu_agent(payload)
    pid = res.get("return", {}).get("pid")
    if not pid:
        raise RuntimeError(f"Failed to exec: {res}")

    # 轮询进程执行状态
    start_time = time.time()
    while time.time() - start_time < timeout:
        status = qemu_agent({"execute": "guest-exec-status", "arguments": {"pid": pid}})
        ret = status.get("return", {})
        if ret.get("exited", False):
            exitcode = ret.get("exitcode", 0)
            out = base64.b64decode(ret.get("out-data", "")).decode("utf-8", errors="replace")
            err = base64.b64decode(ret.get("err-data", "")).decode("utf-8", errors="replace")
            return exitcode, out, err
        time.sleep(0.5)

    raise TimeoutError("Execution timed out")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: win_exec.py '<powershell command>'")
        sys.exit(1)

    cmd_str = sys.argv[1]
    code, out, err = run_powershell(cmd_str)
    if out:
        print(out, end="")
    if err:
        print(f"STDERR: {err}", file=sys.stderr, end="")
    sys.exit(code)

使用方式:

# 检查虚拟机进程
python3 scripts/win_exec.py 'Get-Process'

# 检查内存与系统版本
python3 scripts/win_exec.py 'Get-CimInstance Win32_OperatingSystem | Select-Object TotalVisibleMemorySize, FreePhysicalMemory'

# 下发测试用例并验证返回码
python3 scripts/win_exec.py 'curl.exe -I https://www.google.com'

五、高效测试的“终极姿态”:QCOW2 差分快照秒级还原

进行网络底层修改、驱动安装或破坏性测试时,最优雅的实践是写时复制(Copy-on-Write)差分盘

  1. 制作基础盘(Golden Image): 将已安装好 VirtIO 驱动、Guest Agent、优化完的镜像固化:

    virsh shutdown win10-ltsc
    # 标记为只读基础盘
    chmod 444 /var/lib/libvirt/images/win10-ltsc-golden.qcow2
    
  2. 为每次测试生成秒级派生盘

    qemu-img create -f qcow2 -b /var/lib/libvirt/images/win10-ltsc-golden.qcow2 \
      -F qcow2 /tmp/win10-ephemeral-test.qcow2
    
  3. 测试完毕即丢弃: 无论系统内部注册表、驱动、路由被修改成什么样,直接删除 /tmp/win10-ephemeral-test.qcow2。永远不需要费时费力执行“卸载”或“逆向清理”,从源头上保障 100% 幂等与纯净!