wangz's blog / Tags

如何在无法显卡直通的 KVM 中愉快地使用 Windows

Windows 就应该活在虚拟机里。

1. 「引」

本文简要地描述了怎么在 GNU/Linux 下愉快地使用无显卡直通的 Windows 虚拟机。

2. 在物理硬盘上安装 Windows

网上有大把教程,不再赘述。

另外推荐 Windows 10 Ltsc 2019,该系统受支持到 2029 年。「当然需要获得获得正版授权」。

而且今年发布的 Windows 10 Ltsc 2021 也只有五年支持,还封装了 WSL 之类的组件。我之后的使用场景主要是 Linux Host + Windows Guest,所以 Ltsc 2021 对我来说,非但没有延长支持时间,还带来了累赘,增大了性能消耗。

3. 直通硬盘中的 Windows 到 Qemu 中进行启动

启动命令如下

pkexec qemu-system-x86_64 \
       -name "${QEMU_CLASS}",process="${QEMU_CLASS}" \
       -daemonize \
       -enable-kvm \
       -cpu host,kvm=on,+hypervisor,+invtsc,l3-cache=on,migratable=no,hv_frequencies,kvm_pv_unhalt,hv_reenlightenment,hv_relaxed,hv_spinlocks=8191,hv_stimer,hv_synic,hv_time,hv_vapic,hv_vendor_id=1234567890ab,hv_vpindex,topoext \
       -m 8192 \
       -smp cores=4,threads=2,sockets=1 \
       -machine q35 \
       -drive if=pflash,format=raw,readonly=on,file="$OVMF" \
       -net nic -net user,hostfwd=tcp::${LOCAL_PORT}-:3389 \
       -nodefaults \
       --display none \
       -drive format=raw,file=/dev/nvme0n1 \
       -drive format=raw,file=/dev/sdb

这里直通了 Windows 所在的整个磁盘,这种情况下的 IO 可以说是最好的,消耗很少。比 Qcow2 之类的还要棒。

不需要任何图形,因为之后我们使用 rdp 显示图形。

4. 使用 FreeRDP 链接

由 Gentoo 群的部分群友提示,使用 rdp 链接要比直接使用 spice 更好一些,实际体验确实如此,spice 占用了更多的资源但是表现并不如 rdp 流程。

以下是使用 rdp 进行链接的方法

xfreerdp /u:******** /p:************ /v:127.0.0.1:10322 \
         /w:1600 /h:900 /bpp:32 \
         /dynamic-resolution \
         +clipboard +fonts \
         /gdi:hw \
         /rfx /rfx-mode:video \
         /sound:sys:pulse \
         +menu-anims +window-drag  \
         /drive:home,${GUEST_HOME_DIR} \
         +auto-reconnect \
         /wm-class:"FOO_WINDOW_CLASS" &!

5. 与 i3wm(或者其他 Window Manager) 交互

在 X11 下有这样一个问题,就是因为 qemu 和 rdp 会抓住整个键盘,所以所有快捷键的直接操作对象都是虚拟机中的内容。

这在 KDE/Gnome 下是没啥问题的,因为这种大型 DE 用户的主要操作习惯还是依靠鼠标。但是我的主要操作环境是 i3wm,一个主要依靠 Super 修饰键的窗口管理器。

在该 WM 中,主要通过 Super+<NUM> 来进行工作区的切换,但是由于 Windows 直接拦截了这些快捷键,所以如果在需要切换工作区的话,就需要鼠标点击,或者将鼠标移出 rdp 窗口才能切换工作区,比较繁琐。

之前比较讨厌 xkeysnail 所以,想到的都是使用 Auto Hot Key 在 Windows 中抓到后在使用 rest 之类的方法传到 Host 上,调用 i3-msg 命令进行其他各种操作,相关过程见 qemu/kvm(virtual machine manger) windows10虚拟机和i3wm win+num 切换workspace冲突问题大家有解决过吗

但是最近开始用 60% 的小键盘了,不得不使用 xkeysnail 这样的按键来将映射一批方向键(emacs 风格的),所以针对使用 xkeysnail 的用户有一套更简单的方式,因为 xkeysnail 直接在 evdevuinput 上运行的,所以就可以直接在该层读取后直接调用 i3-msg 命令。

5.1. xkeysnail 配置参考如下:

define_keymap(re.compile(".*"), {
    # 用来切换 windows guest 所在的工作区和其他工作区的热键
    # 其中的 =/home/chin/.scripts= 是我的常用脚本目录
    # 脚本在下节给出了
    K("Super-ESC"): launch(["/home/chin/.scripts/show_windows"])
}, "All windows")

define_keymap(re.compile("FOO_WINDOW_CLASS"), {
    K("Super-KEY_1"): launch(["i3-msg", "workspace", "1"]),
    K("Super-KEY_2"): launch(["i3-msg", "workspace", "2"]),
    K("Super-KEY_3"): launch(["i3-msg", "workspace", "3"]),
    K("Super-KEY_4"): launch(["i3-msg", "workspace", "4"]),
    K("Super-KEY_5"): launch(["i3-msg", "workspace", "5"]),
    K("Super-KEY_6"): launch(["i3-msg", "workspace", "6"]),
    K("Super-KEY_7"): launch(["i3-msg", "workspace", "7"]),
    K("Super-KEY_8"): launch(["i3-msg", "workspace", "8"]),

    # 使用 Mod + Shift + q 直接关闭虚拟机内的程序,模拟 Alt+F4
    K("Super-Shift-q"): K("M-F4"),
}, "Windows guest")

5.2. 切换当前工作区和 Windows Guest 所在工作区的脚本

以下的 bash 函数可以做到在 windows guest 工作区和当前工作区切换的功能。

  • 但是需要注意的是 i3wm 需要开启 workspace_auto_back_and_forth yes
  • 将以下脚本保存到 $HOME/.scripts/show_windows 文件
    保存到什么位置都可以,只要和 xkeysnail 配置对应即可。
#!/usr/bin/env bash

show_windows() {
    local num=$(i3-msg -t get_tree | jq -r '.nodes[].nodes[] | recurse(.nodes[], .floating_nodes[]) | select(.type == "workspace")  | .num as $WSN |  recurse(.nodes[], .floating_nodes[]) | select(.window_properties.class == "FOO_WINDOW_CLASS") | $WSN')
    i3-msg "workspace $num"
}

show_windows

上文中的 FOO_WINDOW_CLASS 需要进行修改,匹配自己的目标 Window Class.

Powered by Org-mode and a fork of org-static-blog