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 直接在 evdev
和 uinput
上运行的,所以就可以直接在该层读取后直接调用 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.