我的 CachyOS 装在移动固态上,平时通过电脑启动菜单进入。最近想让 Windows 和 CachyOS 同时运行,电脑上正好有 VMware,就尝试把这块盘交给虚拟机启动。

第一次接通后,原来的 KDE 桌面确实起来了。随后我在虚拟机里更新系统,重启却进了应急模式。修好内核后又遇到登录黑屏,折腾到凌晨才重新回到桌面。这篇文章记录当时看到的日志、实际做过的修复,以及几个容易在操作时踩到的坑。

把移动盘里的系统接入 VMware

这次使用的环境如下:

项目

配置

宿主机

Windows 11,i7-13620H,32 GB 内存,RTX 4060

VMware

Workstation 17.6.3,UEFI,4 个虚拟 CPU,8 GB 内存

外置盘

约 1 TB,GPT 分区,4 GB EFI 分区和约 927 GB Linux 分区

客机系统

CachyOS,KDE Plasma,根文件系统为 Btrfs

启动工具

Limine,limine-mkinitcpio-hook

网络

NAT,虚拟网卡为 e1000e

VMware 通过原始磁盘映射访问外置盘,沿用盘上已有的 EFI 分区和 Linux 系统。客机里的安装、更新和文件修改会直接写到这块盘上。

启动脚本按磁盘序列号、容量和 USB 类型识别外置盘,再生成对应的物理盘映射。这样重新插拔后,即使 Windows 分配的磁盘编号发生变化,也能找到原来的设备。启动虚拟机前,Windows 将外置盘置为脱机,由 VMware 访问它。切回原生启动时,要先让客机完整关机,再退出 VMware。

配置过程中还出现过一次“Ethernet0 没有可用的 PCIe 插槽”。补齐虚拟机的 PCIe bridge 配置后,才继续启动到 Linux。这一段发生在客机开机之前,后面的应急模式和黑屏则需要查 Linux 日志。

鼠标操作也让我困惑了一会儿。点进虚拟机后,鼠标被捕获,按 Esc 没有反应。VMware 窗口底部其实写着释放方法:按 Ctrl + Alt,键盘和鼠标就会回到 Windows。

重启后卡在应急模式

屏幕上出现的是:

You are in emergency mode.
Enter root password for system maintenance

重启几次都回到这里。进入维护终端后,先看失败单元和本次启动日志:

systemctl --failed --no-pager
journalctl -b -u boot.mount --no-pager
uname -r
ls /usr/lib/modules

失败单元里有 boot.mount 和 ufw.service。日志还反复出现模块文件找不到、zram 设备等待超时,以及 /boot 挂载失败。

最早启动的是旧 LTS 内核,后来切到主内核,情况也一样。两次检查得到的版本关系是:

内核

当时启动的版本

包管理器记录的版本

主内核

7.0.9-1-cachyos

linux-cachyos 7.2.8-2

LTS 内核

6.18.32-1-cachyos-lts

linux-cachyos-lts 6.18.52-1

ls /usr/lib/modules 还能列出一些旧版本目录,不过目录里的模块文件已经缺失;新主内核对应的模块目录也没有完整落盘。对正在运行的 7.0.9-1-cachyos 执行 modprobe vfat,就能看到具体的问题:

.../kernel/fs/fat/fat.ko.zst ... No such file or directory
.../kernel/fs/fat/vfat.ko.zst ... No such file or directory
modprobe: ERROR: could not insert 'vfat': Unknown symbol in module,
unknown parameter (see dmesg)

再挂载 /boot,结果是:

mount: /boot: unknown filesystem type 'vfat'.

这块 EFI 分区使用 FAT 文件系统,挂载在 /boot。当前内核找不到配套的 FAT 模块,于是挂载失败,启动停在维护状态。modprobe 会按运行内核的版本查找模块,并使用 depmod 生成的依赖索引加载所需依赖。modprobe 手册

我刚在 VMware 里更新过内核,所以继续翻了 /var/log/pacman.log。最后一行给出了更直接的证据:

[2026-10-03T23:56:02+0800] [ALPM] transaction interrupted

前面已经记录了主内核、LTS 内核及 NVIDIA 模块的版本更新,事务却在后续软件包处理期间中断了。

到这里可以确认,系统更新确实中断过,内核安装记录、模块文件和启动镜像没有一起更新完成。具体是什么打断了更新,现有记录没有给出原因。

从缓存恢复 FAT 模块并挂载 EFI 分区

当时根文件系统还能访问,pacman 的缓存也保留着旧内核包,这让修复省了不少事。

第一步是恢复正在运行的旧主内核模块,让系统具备挂载 EFI 分区的能力。下面这些命令整理自当时的维护操作,在 root 的 Bash 中执行。包名和版本对应这一次故障,其他机器需要先核对自己的 uname -r 和缓存文件。

repair_pkg=/var/cache/pacman/pkg/linux-cachyos-7.0.9-1-x86_64_v3.pkg.tar.zst

mount -o remount,rw /
bsdtar -xpf "$repair_pkg" -C / usr/lib/modules/7.0.9-1-cachyos/
depmod -a 7.0.9-1-cachyos
modprobe vfat
LC_ALL=C mount /boot
findmnt /boot

这里解压的是旧内核的模块目录。depmod 随后重新扫描模块,生成依赖索引;指定版本号时,它处理该版本的模块目录。depmod 手册

这一步成功后,findmnt 显示:

TARGET SOURCE    FSTYPE OPTIONS
/boot  /dev/sda1 vfat   rw,relatime,...

/boot 已经挂到了真正的 EFI 分区上,而且可写,可以继续恢复启动文件。

重装新内核并生成启动镜像

缓存里也有这次更新下载的新包。确认文件存在后,重装了主内核、LTS 内核,以及各自对应的 NVIDIA Open 模块:

pacman -U \
  /var/cache/pacman/pkg/linux-cachyos-7.2.8-2-x86_64_v3.pkg.tar.zst \
  /var/cache/pacman/pkg/linux-cachyos-lts-6.18.52-1-x86_64_v3.pkg.tar.zst \
  /var/cache/pacman/pkg/linux-cachyos-nvidia-open-7.2.8-2-x86_64_v3.pkg.tar.zst \
  /var/cache/pacman/pkg/linux-cachyos-lts-nvidia-open-6.18.52-1-x86_64_v3.pkg.tar.zst

pacman -U 可以从本地软件包文件安装或重装。这里保留了 NVIDIA 模块,因为这块盘还会在带 RTX 4060 的电脑上原生启动。pacman 手册

此前尝试生成启动镜像时,屏幕刷出了大量 module not found,最后提示:

WARNING: No modules were added to the image.
ERROR: mkinitcpio failed for kernel 7.2.8-2-cachyos, skipping.

当时新内核模块还缺着,继续重建镜像只能重复报错。重装软件包后,构建才出现 Initcpio image generation successful,新的 vmlinuz 和 initramfs 被复制到 /boot,limine.conf 也更新了。

CachyOS 的 Limine 内核条目由 limine-mkinitcpio-hook 管理。需要单独重建时,可以运行 limine-mkinitcpio,它会调用 mkinitcpio 并更新启动条目。CachyOS 启动管理器文档

这一步还出现过 Snapper 的 D-Bus FileNotFound 提示。当时系统处于维护环境,我先记录了它,继续检查内核镜像的构建结果;后来完整升级时,Snapper 的前后快照都成功执行了。

内核起来了 登录界面却黑屏

再次启动主内核后,应急模式消失了,系统已经运行在 7.2.8-2-cachyos 上。Windows 能 ping 通客机,图形界面却一直黑着。

切到 TTY 后,可以正常登录。VMware 会处理 Ctrl + Alt 组合键,切控制台时可以先按住 Ctrl + Alt,按一下空格,再保持前两个键按下并按 F3。Broadcom 的说明中记录了这套按键方法。切换 Linux 虚拟机文本控制台

接下来查看登录服务:

systemctl --failed --no-pager
systemctl status display-manager --no-pager
sudo journalctl -b -p err -n 40 --no-pager

系统级失败单元是 0,display-manager 指向的 plasmalogin.service 也显示 active (running)。不过日志里的 greeter 和壁纸进程已经崩溃:

This application failed to start because no Qt platform plugin
could be initialized.

KWin 的登录会话还出现了启动超时和打开 DRM 设备失败的记录。继续检查动态库依赖:

ldd /usr/bin/kwin_wayland | grep 'not found'

这条命令没有输出缺失依赖。结合前面的更新中断,后续检查集中到了图形组件的安装状态。

期间也试过开启 VMware 的 3D 加速。先完整关闭虚拟机,备份 .vmx,再加入:

mks.enable3d = "TRUE"

宿主日志确认 3D 已启用,重新开机后仍然黑屏。随后准备完成全系统更新,并重装 Mesa、LLVM、Qt 和登录组件:

sudo pacman -Syu mesa libdrm llvm-libs qt6-base qt6-wayland kwin plasma-login-manager

安装计划里有两项很显眼:plasma-login-manager 从 6.6.5-1.1 更新到 6.7.4-3,qt6-wayland 则列为新增安装。这个组合与前面的 Qt 平台插件错误相符,也说明还需要把中断的更新做完。

文件冲突挡住了剩余更新

这一轮更新在检查文件冲突时停住了。最初的输出还包含 systemd-sysvcompat 的 init.1.gz,后续重试只剩下 PipeWire 的这个文件:

error: failed to commit transaction (conflicting files)
pipewire-pulse: /usr/share/pipewire/pipewire-pulse.conf.avail/20-upmix.conf
exists in filesystem
Errors occurred, no packages were upgraded.

先查文件归属:

LC_ALL=C pacman -Qo /usr/share/pipewire/pipewire-pulse.conf.avail/20-upmix.conf

输出是:

error: No package owns /usr/share/pipewire/pipewire-pulse.conf.avail/20-upmix.conf

随后将它复制到独立的备份目录,再让 pacman 覆盖这一条路径:

sudo bash
export LC_ALL=C

backup_dir=$(mktemp -d /root/kf-update-backup.XXXXXX)
cp -a /usr/share/pipewire/pipewire-pulse.conf.avail/20-upmix.conf "$backup_dir/"

pacman -Syu mesa libdrm llvm-libs qt6-base qt6-wayland kwin plasma-login-manager
–overwrite usr/share/pipewire/pipewire-pulse.conf.avail/20-upmix.conf

-Qo 查询安装数据库中的文件归属,--overwrite 按给定路径模式处理文件冲突。这次只指定了已经查询、备份过的 20-upmix.conf。pacman 手册

实际处理到这里,我已经被手输长命令折腾得够呛:有一次把 .pkg.tar.zst 打成了 .pkg.tat.zst,还有一次把多行命令挤在一行,反斜杠转义了后面的空格,pacman 收到的文件名就多出了空白。终端里的中文又显示成方块,报错读起来更费劲。后续命令加上 LC_ALL=C,至少能稳定读到英文输出。

用一条短命令完成后半段修复

这次排查是我和 ChatGPT 一起做的。前半段靠截图、日志和手动输入命令推进,到了完整更新这一步,改成了脚本。

当时 SSH 还没有连通,VMware Tools 的客机连接也没有确认可用,所以在 Windows 的 VMnet8 地址上临时开了一个 HTTP 服务,把准备好的 Bash 脚本传进去。我只需要在客机里输入:

curl -f 192.168.137.1/r | sudo bash

这是当时临时服务的地址,修复结束后已经关闭。

脚本先确认运行环境、/boot 的挂载状态和 pacman 锁文件,再查询冲突文件的归属并备份。接着完成系统升级、检查软件包文件,最后重启登录服务。输出同时保存在客机里,并通过 VMware 内网回传到 Windows,方便持续查看进度。

日志显示,这一轮处理了 146 个软件包。原先卡住的文件冲突顺利通过,Qt Wayland 组件安装完成,登录管理器也更新了。随后主内核和 LTS 内核的启动镜像都构建成功,Snapper 完成了升级前后的快照。

更新结束后,检查了这次涉及的内核与图形软件包:

pacman -Qk mesa libdrm llvm-libs qt6-base qt6-wayland kwin \
  plasma-login-manager linux-cachyos linux-cachyos-lts
systemctl --failed --no-pager

这些包都返回 0 missing files,失败单元数也是 0。-Qk 检查软件包登记的文件是否存在;这次记录的是文件存在性检查结果。pacman 手册

重启登录服务:

systemctl restart plasmalogin.service

这次终于看到了登录页。输入密码后,KDE 桌面正常显示,终端和浏览器都能打开。修复脚本退出码为 0,备份及完整日志留在客机的 /root/kf-update-backup.4oFNar/。

桌面恢复发生在补齐图形组件、完成系统更新并重启登录服务之后。此前开启过 3D 加速,单独做完这项调整时黑屏仍然存在;恢复这轮同时改动了多个软件包,具体哪一项直接触发了原先的崩溃,还没有逐项复现。

桌面恢复后的几个使用细节

接着处理 AUR 更新时,终端忽然开始显示 Clash Verge 的安装脚本。顶部写着:

Paging with less. Press 'q' to quit or 'h' for help.

这是更新工具打开的代码预览,按小写 q 就能退出当前页,继续更新流程。post_upgrade() 和 pre_remove() 是安装脚本里的函数名,页面会把它们直接显示出来。

这轮修复也让我更愿意保留一段时间的 pacman 缓存。应急模式下网络和驱动都不太方便处理,缓存里的旧内核包帮我恢复了 FAT 模块,新内核包又用来补齐安装。下次内核更新后,准备先确认启动镜像生成成功、正常重启过,再清理旧包。

我已经在 CachyOS 上装好了 ChatGPT,之后准备让 Windows 和 CachyOS 各自使用一份工作目录,通过自建 Gitea 提交、推送和拉取代码。外置盘继续承担原生启动和 VMware 客机这两种使用方式。

这篇记录写到成功进入 KDE 桌面为止。

おとといは兎を見たの、昨日は鹿、今日はあなた