☰
Linux 更换内核后 VMware 模块编译失败?完整排查与修复指南
2026/10/17 8:04:29 网站建设 项目流程

在 Linux 上跑 VMware Workstation 的老用户,多半都跟下面这句话打过照面:

Before you can run VMware, several modules must be compiled and loaded into the running kernel.

字面意思是:在运行 VMware 之前,需要先编译几个内核模块,并加载到当前正在运行的内核里。说人话版本就是:你的内核换了,VMware 的“驱动”没跟上,得重新编一次才能继续用。我这次就是在一次更换内核的操作之后遇到这个报错的——系统还是那个系统,内核从 A 换成了 B,VMware 就当场罢工了。

这篇文章不打算讲怎么“避开”这个问题,因为只要你在 Linux 上用 VMware,就躲不开这个机制。我会把排查思路、修复命令、以及那些让我多折腾了几个小时的隐藏细节全写出来。适合正在被这个报错卡住、或者准备升级内核 / 切换内核的朋友直接参考。

1. 报错触发的那一刻:从“换内核”到 VMware 罢工之间发生了什么

先复盘一下这个报错通常会在什么时间点蹦出来。最常见的触发场景有这么几类:

  • 发行版自动更新内核:比如 Ubuntu 从 6.5 升到 6.8,重启后第一次打开 VMware 就报错。
  • 手动更换内核:自己装了一个第三方内核或者自编译内核,然后从 GRUB 里选新内核启动。
  • GRUB 里选错了内核项:机器上同时装了多个内核版本,切到旧内核或新内核时,模块对不上。
  • 升级 VMware Workstation 本体:有时内核没换,但 VMware 自带的模块源码变了,一样会触发重新编译。

这个报错对话框还有一个特点:它会直接给你指一条路,通常写着让你以 root 身份运行/usr/bin/vmware-modconfig --console --install-all。很多人看到这条提示不敢动,或者动了但没成功,就卡在原地。

要理解它为什么不直接自动修复,得先说清楚 VMware 在 Linux 上的工作方式。VMware Workstation 并不是一个完全用户态的软件,它需要往当前运行的内核里加载一组内核模块,主要就是两个:vmmon和vmnet。你可以把它们理解成 VMware 的底层驱动。

vmmon负责虚拟机监视相关的工作,比如虚拟 CPU 的调度、内存虚拟化、特权指令捕获;vmnet负责虚拟网络功能,NAT、桥接、Host-only 全靠它。这两个模块没加载,虚拟机根本起不来,或者起来了也没有网络。

而 Linux 内核模块和当前内核之间的关系是“严格绑死”的。内核从 A 版本变成 B 版本之后,A 版本编译出来的.ko文件在 B 版本下就是不能用的。这不是 VMware 故意刁难,而是 Linux 内核本身的设计原则:内核模块的接口没有稳定的 ABI 承诺,模块编译时依赖的数据结构、函数签名、头文件布局都可能随内核版本变化,旧模块加载进新内核可能导致崩溃甚至数据损坏。所以内核在加载模块前会做严格校验,校验不通过直接拒绝执行。

换句话说,看到这个报错虽然不是好消息,但它其实是个保护机制。它提前告诉你说:当前内核里的 VMware 模块状态不对,你先修好了再开虚拟机,省得跑到一半崩溃。

2. 根因拆解:VMware 内核模块与 Linux 内核版本之间的“契约”

2.1 内核模块的“版本魔数”机制

Linux 内核模块不是随便一个.ko文件就能加载的。编译模块的时候,编译器会把一组描述信息写进模块文件里,其中有一个东西叫 vermagic(version magic,版本魔数),它记录了编译该模块时依赖的内核版本特征。

用命令可以直接看到:

modinfo /lib/modules/$(uname -r)/misc/vmmon.ko 2>/dev/null || echo "模块文件不存在"

如果文件存在,输出里会有一行类似:

vermagic: 6.8.0-45-generic SMP preempt mod_unload modversions

这一串内容包括内核版本号、是否 SMP、是否可抢占、模块卸载支持等标记。内核在加载模块时,会把这个 vermagic 跟当前运行内核的特征做比对,任何一个关键项不一致,就拒绝加载。报错信息往往是:

Error: Invalid module format

用生活里的话说,这就像手机系统升级之后,某个 App 的插件还是按老系统接口写的,系统直接不认,而不是等插件运行时崩溃了再怪它。

2.2 为什么模块不能“一次编译,到处运行”

有人可能想问:VMware 为什么不把模块编得兼容所有内核?

答案很简单:做不到,也没必要。Linux 内核的导出函数和内部数据结构随时在调整,有些头文件里的结构体大小一改,整个模块的内存布局就对不上了。要让一个模块兼容“所有内核”,等于给每种内核单独做一份适配,维护成本极高。VMware 采用的策略是:安装一个内核版本对应的头文件,现场编译,编出来的模块只对这个内核负责。

而且你仔细看报错框里的英文提示,它自己也说了这是正常流程(This is a normal process)。换内核之后重新编译模块,本来就是 VMware 在 Linux 上运行的常规操作。

2.3 编译一个内核模块需要三样东西

要现场编译vmmon和vmnet,缺一不可的是:

  1. 当前运行内核的头文件/构建树
  2. 完整的编译工具链(gcc、make 等)
  3. root 权限

很多人在手动更换内核之后,只安装了内核本体,没有安装对应的 linux-headers 或 kernel-devel 包,导致vmware-modconfig一开工就报“找不到内核头文件”。这一步是最常见的失败原因,没有之一。

3. 一步步排查:确认内核版本、头文件与编译工具链

先别急着跑修复命令。我踩过几次坑之后总结出来的经验是:先做三分钟静态检查,确认环境缺什么,再动手。这样能避免反复盲目执行命令浪费时间。

3.1 第一步:确认当前运行的内核版本

uname -r

把输出记下来,比如6.8.0-45-generic。后面所有操作都以这个版本号为准。注意,这里说的是“当前运行的内核”,不是最新安装的内核。如果你刚升级内核但还没重启,那uname -r显示的还是旧内核,此时 VMware 可能并不会报错;一旦重启进入新内核,问题才出现。

3.2 第二步:确认内核构建树是否存在

内核模块编译时需要一个 build 目录,发行版通常通过/lib/modules/$(uname -r)/build这个软链接指过去。检查:

ls -ld /lib/modules/$(uname -r)/build

如果提示No such file or directory,说明内核头文件没有安装。这时候先补包,不要继续往下走。

如果你的电脑上同时存在多个内核,一定要确认这个路径里的版本号和uname -r一致。我遇到过一种情况是:头文件确实装了,但装的是另一个内核版本的,build 链接指向不存在的目录,编译照样失败。

3.3 第三步:按发行版确认缺什么包

不同发行版、不同包管理器的命名习惯不一样,整理成表如下:

发行版需要安装的包备注
Ubuntu / Debianlinux-headers-$(uname -r)也可以装linux-headers-generic系列跟随内核更新
Ubuntu 启用 HWE 内核linux-headers-generic-hwe-22.04等注意 HWE 内核不能用 generic 头文件
RHEL / CentOSkernel-devel、kernel-headers版本必须和 uname -r 完全对应
Fedorakernel-devel版本会自动匹配当前内核
Arch Linuxlinux-headers如果用 LTS 内核,对应linux-lts-headers
openSUSEkernel-devel也需要注意版本对应

举例,Ubuntu 下典型操作:

sudo apt update sudo apt install linux-headers-$(uname -r)

RHEL/CentOS 系:

sudo yum install kernel-devel kernel-headers

安装完再次检查ls -ld /lib/modules/$(uname -r)/build,确认链接有效。

3.4 第四步:确认工具链可用的 gcc 和 make

gcc --version make --version

内核模块编译对编译器版本有要求。一般来说,gcc 大版本一致就能编译,但有时候 gcc 版本太新会导致编译时出现一堆警告,甚至被当成错误中断。查看当前运行内核是用哪个 gcc 编译的:

cat /proc/version

输出里会有类似gcc (Ubuntu 12.3.0-1ubuntu1) 12.3.0的信息。如果系统默认 gcc 和这个版本差异过大,后续编译失败时就优先怀疑这里。

这三分钟排查做完,你基本就知道问题出在哪儿了:要么缺头文件,要么工具链版本不对,要么就是后面要说的 Secure Boot 这类“隐形坑”。

4. 手动修复 VMware 内核模块的完整操作

4.1 主角命令:vmware-modconfig

补完头文件之后,核心操作就是执行 VMware 官方提示的那条命令:

sudo /usr/bin/vmware-modconfig --console --install-all

--console表示把编译输出打到终端,方便看进度;--install-all表示一次性处理所有需要构建的模块。执行过程中你会看到类似:

Stopping VMware services... Building VMware Tools modules... Extracting the sources of the vmmon module. Building the vmmon module. Extracting the sources of the vmnet module. Building the vmnet module. Installing the vmmon module. Installing the vmnet module.

看到Installing the ...结束,说明编译安装完成。这里必须用 sudo,因为模块要写入/lib/modules/$(uname -r)/misc/目录,普通用户没有写权限。

4.2 验证模块是否真的加载成功

很多人在编译命令成功输出之后就急着打开 VMware,结果还是报错,原因是没有确认模块是否真正加载进内核。验证方法:

lsmod | grep vm

如果有vmmon和vmnet两行输出,说明加载成功。如果没有,说明编译产物虽然生成了,但加载环节出了问题,需要回到dmesg看具体原因。

也可以直接检查模块文件是否存在于正确位置:

ls -l /lib/modules/$(uname -r)/misc/vmmon.ko

再刷一下模块依赖表:

sudo depmod -a

然后再次lsmod | grep vm。如果这时候还没有,往下看加载失败的具体报错。

4.3 编译失败时,日志在哪里看

vmware-modconfig的日志默认写在/tmp/vmware-root/下。排查命令:

ls -lt /tmp/vmware-root/

找到最新的vmware-modconfig-*.log文件,看末尾内容:

sudo tail -80 /tmp/vmware-root/vmware-modconfig-*.log

日志里如果出现Unable to find the kernel header,说明头文件路径不对;如果出现No rule to make target,通常说明内核构建树不完整;如果出现GCC version ... is not supported,则是编译器版本问题。

4.4 自己编译内核的人要注意一个额外步骤

如果你不是用发行版官方内核,而是手动编译、安装的内核,前面的 linux-headers 包就未必存在了。你需要准备与当前运行内核完全对应的内核源码树,并且在源码目录里执行过:

make modules_prepare

这一步会生成模块编译所需的中间文件和头文件。没做过modules_prepare的话,源码树是不完整的,vmware-modconfig依然可能报到“找不到头文件”或者“make 失败”。自编译内核的用户经常卡在这里,以为是 VMware 的问题,其实内核源码树自己没准备完整。

4.5 一次修复失败的常见处理顺序

如果执行vmware-modconfig --console --install-all失败,我习惯按下面的顺序排查:

  1. 先看终端输出,是vmmon失败还是vmnet失败,还是两个都失败。
  2. 去/tmp/vmware-root/找日志文件,看具体的 make 错误行。
  3. 确认/lib/modules/$(uname -r)/build链接有效,且版本与uname -r一致。
  4. 确认gcc版本与/proc/version显示的内核构建编译器版本接近。
  5. 如果日志指向权限问题,检查dmesg里有没有 Secure Boot 相关的拦截记录。

这套顺序能覆盖绝大多数失败场景,不用反复盲目重试。

5. 那些让我多折腾两小时的隐藏坑(Secure Boot、DKMS 与 GCC 版本)

5.1 Secure Boot 是一个“隐形拦截者”

有一种情况极其隐蔽:模块编译全部成功,vmmon.ko和vmnet.ko也都在正确路径下,但lsmod就是没有输出,打开 VMware 仍然报错。我用dmesg查看之后,发现里面有这样的记录:

Lockdown: modprobe: vmmon is restricted; see man kernel_lockdown.7 Lockdown: modprobe: vmnet is restricted; see man kernel_lockdown.7

问题出在 UEFI Secure Boot。安全启动开启时,内核默认拒绝加载没有有效签名的模块。VMware 现场编译出来的模块是本地生成的,没有经过签名,加载请求直接被内核拦截了。编译没问题,加载才是问题。

解决思路有两种。第一种最简单:进 BIOS/UEFI 设置里关闭 Secure Boot,适用于个人机器。第二种是不关闭安全启动,而是通过 MOK(Machine Owner Key)机制告诉内核允许加载未签名模块:

sudo mokutil --disable-validation

这条命令执行后,系统会要求设置一个一次性密码,然后重启。重启时会出现蓝色 MokManager 界面,选择对应菜单、输入刚才的密码确认操作,系统才会真正生效。重启之后再跑一遍sudo vmware-modconfig --console --install-all,模块就能正常加载了。

如果是在不方便改 BIOS 设置的场景下,最好先确认环境里是否有强制开启 Secure Boot 的策略,这个坑容易把排查时间拖得非常长。

5.2 GCC 版本不匹配:编译器也要“门当户对”

有段时间我的系统默认 gcc 是 13,而当前内核是用 gcc-12 构建的。vmware-modconfig编译模块时虽然没直接崩溃,但报了一个GCC version ... is not supported的警告,紧接着编译失败。查了 VMware 的日志之后发现,它对编译器版本有明确的检测逻辑,gcc 版本太新会被判定为不受支持。

处理方式有两种。一是安装与内核构建版本一致的 gcc:

sudo apt install gcc-12

二是编译时显式指定编译器:

CC=gcc-12 sudo vmware-modconfig --console --install-all

还遇到过一种情况是系统里有多个 gcc 版本,update-alternatives指向的并不是你想要的版本,编译时用CC=版本号这种方式反而更直接。

5.3 换内核之后的重编译,能不能自动化

一个很自然的想法是:能不能像 DKMS 管理其他驱动那样,让 VMware 模块自动跟随内核重建?

VMware Workstation 自带的机制和 DKMS 之间是有区别的。DKMS 会在内核版本变化时重新编译注册过的模块源码,而 VMware 自带脚本默认不做这种自动跟踪。所以每次换内核之后,面前这个报错才会准时出现。

如果你频繁切换内核,可以在/usr/lib/vmware/modules/source/下找到vmmon.tar和vmnet.tar这两个源码包,把它们手动注册到 DKMS 里。但这个流程需要自己编写dkms.conf,而且 VMware 升级后源码会被替换,注册信息可能失效,维护起来有点麻烦。

更实用的做法是:写一个小的检查脚本,判断lsmod里有没有vmmon,没有就自动执行修复命令。放在本地定时任务或者手动调用都行。不过我的建议是,先接受“换内核后必须重编模块”这个设定,把重编命令记在常用命令列表里,反而是最少踩坑的方式。

5.4 四个高频日志关键词的快速对照表

日志/现象通常原因解决方向
Unable to find the kernel header内核头文件未安装安装对应发行版的 linux-headers / kernel-devel
No rule to make target内核构建树不完整自编译内核则执行make modules_prepare
GCC version ... not supported系统 gcc 版本与新内核构建版本差异过大安装匹配版本或CC=gcc-XX指定
Permission denied / Operation not permitted权限不足或 Secure Boot 拦截检查 sudo,检查mokutil --sb-state,必要时导入 MOK

这个小表格基本能覆盖 90% 以上的恢复场景。剩下不常见的问题,比如磁盘空间不足、内存太小导致编译中断,日志里也会写清楚。

6. 经历过这轮折腾之后,我留下的一些习惯

如果这篇内容能帮你少走弯路,那最后我再啰嗦几句实际操作总结出的习惯。

第一,升级内核之后不要急着重启。在重启之前就先跑一次sudo vmware-modconfig --console --install-all,确认新内核里的模块就绪,再重启进入新系统。这样 VMware 不会在重启后甩你一脸报错。当然如果头文件还没跟着新内核准备好,这一步可能会提前暴露问题,总比重启之后手忙脚乱好。

第二,GRUB 里尽量保留上一个可用的内核项。万一新内核有问题,启动时选择旧内核,旧内核对应的 VMware 模块还在,虚拟机立刻能跑。这个回退路径在急着用虚拟机的时候非常救命。清理旧内核要谨慎,别一顿操作把现在运行的内核相关文件删了。

第三,每次处理完这个问题之后,顺手把/proc/version的记录和当前 gcc 版本核对一遍。很多编译类报错看似是 VMware 的问题,底子其实是内核构建工具链不一致。养成这个习惯之后,不只是 VMware,其他需要编译内核模块的软件出问题,你也能更快定位。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询