Ubuntu安装NVIDIA驱动报错Building kernel modules的排查与解决 很多朋友在 Ubuntu 上给 NVIDIA 显卡装驱动最常遇到的“劝退点”不是下载速度也不是版本选择而是安装进行到一半时终端突然蹦出一行大写的英文ERROR: An error occurred while performing the step: “Building kernel modules“. See /var/log/nvidia-installer.log我第一次见到这个报错第一反应和很多人一样驱动文件是不是下载坏了于是删掉重下、换个版本再跑结果还是原封不动卡在同一句话上。后来老老实实去翻日志才发现问题根本不在安装包而在内核头文件、编译工具链、Secure Boot 状态和旧驱动残留这一堆“周边环境”上。这篇文章就把这套排查思路完整写出来这个错误到底发生在哪一步、为什么会触发、怎么根据日志快速定位以及一条我实测下来最稳的处理流程。如果你也卡在这个报错上千万别急着重装系统往下看基本能解决。1. 报错场景还原卡在“Building kernel modules”究竟意味着什么1.1 安装过程中的真实阶段NVIDIA 的.run安装包不是简单地把文件复制到系统里它内部有一套完整流程先检查依赖再解压驱动源码和二进制文件然后调用 DKMS 或本地构建脚本来编译内核模块之后尝试加载模块最后写 X Server 配置。普通用户看到的进度条能走到 80% 以上往往恰恰是在最后这一段才失败。“Building kernel modules”翻译过来就是“正在构建内核模块”。这一步是 NVIDIA 驱动和 Linux 内核真正开始打交道的地方。驱动不是一整块静态二进制而是由若干内核模块文件组成最典型的有nvidia.ko、nvidia-modeset.ko、nvidia-drm.ko。安装程序需要把这些.ko文件编译成当前运行内核能识别、能加载的格式所以这一步本质是一次“现场编译”。如果编译环境有问题安装器就会抛出那句经典废话“An error occurred while performing the step: Building kernel modules.”。它并没有告诉你具体原因真正的原因全部写在后半句提示的/var/log/nvidia-installer.log里。1.2 为什么这一步是所有阶段里最容易翻车的说句实话NVIDIA 驱动安装器在“复制文件”“更新配置”这些阶段几乎不会出问题因为这些动作无非是把文件放到固定位置。内核模块构建则完全不同它依赖很多外部条件当前内核版本对应的头文件kernel headers必须存在编译器版本要和构建内核时使用的工具链兼容DKMS 状态必须干净不能有旧版本的残留记录如果开启了 Secure Boot模块签名流程可能中断安装系统里若有正在加载的旧 NVIDIA 模块或者 Nouveau 开源驱动也可能干扰后续加载阶段。任何一个环节不对安装器都可能在这个步骤直接终止。这个报错就像一个路由器上的总警告灯亮起来只告诉你“网络不通”但网线、光猫、DNS、防火墙都有可能。所以排查的关键不是反复重装驱动而是先看懂日志再按顺序排除环境问题。2. 根因排查构建内核模块失败的核心原因2.1 第一优先级内核头文件缺失或版本不匹配内核头文件是构建模块时最重要的依赖。它本质上是当前内核开放给外部模块的接口定义包含了大量.h头文件以及Makefile等构建辅助文件。NVIDIA 驱动的源代码要编译成.ko第一步就是找到这些头文件。这里要特别注意一个细节uname -r输出的内核版本比如6.8.0-51-generic必须等同存在于/usr/lib/modules/下的目录名。如果你的系统只有一个内核但没装对应的linux-headers包那么构建时会出现类似“Unable to find the kernel source tree”的日志接着失败。我见过很多人误以为装了linux-libc-dev就足够了这是不对的。linux-libc-dev提供的是用户态 C 库所需的头文件和内核模块构建需要的linux-headers-$(uname -r)不是一回事。正确做法是sudo apt update sudo apt install -y linux-headers-$(uname -r)安装完成后检查一下软链接是否正常ls -l /lib/modules/$(uname -r)/build如果链接指向的目录不存在说明 headers 包没装对或者装完又被清理了。需要重新安装后再继续。2.2 编译器gcc与内核工具链不一致内核模块是用 C 语言写的编译工具链默认是 gcc。但这里有个隐含要求编译内核模块的 gcc 版本最好和当初编译当前内核时使用的 gcc 版本接近甚至一致。因为内核源码里有一些代码会检测编译器版本不同版本对类型检查、内建函数、宏展开的处理逻辑可能有差异。你可以用两个命令快速对比gcc --version cat /proc/version/proc/version里会显示当前内核是哪个 gcc 编译的。如果系统默认 gcc 是 13而内核是 gcc-12 编译的安装器日志里通常会出现一行警告Kernel compiler gcc-12 is not the same as the compiler used to build the kernel gcc-13。这种警告不一定会直接导致失败但如果后面跟着报错比如unrecognized command-line option或error: implicit declaration of function十有八九就是编译器版本问题。解决办法是安装对应版本的 gcc例如sudo apt install gcc-12然后在运行 NVIDIA 安装器时尝试指定编译器sudo sh ./NVIDIA-Linux-x86_64-xxx.run --kernel-ccgcc-12需要说明的是不同版本的安装器对参数的兼容性不完全一样有的用--kernel-cc有的用--cc如果参数不被识别安装器会给出提示。我最常用的做法是先在日志里确认 CC 变量实际用的是哪个编译器再针对性调整。2.3 Secure Boot 和 Nouveau 的隐藏干扰如果你是在 UEFI 模式下安装 Ubuntu并且系统开启了 Secure Boot那么构建出的内核模块如果没有经过签名重启后内核会拒绝加载。有些版本会在模块构建完成后进入 MOK 注册流程弹出一个需要确认的界面如果用户没注意到安装过程也可能中断在看似“构建模块”的阶段。建议先检查mokutil --sb-state如果输出是SecureBoot enabled最简单的办法是到 BIOS 里临时关闭 Secure Boot装好驱动后再决定是否需要重新开启并配置签名。个人实测下来测试环境关闭 Secure Boot 最省心。另一个常见干扰源是 Nouveau 开源驱动。Ubuntu 默认带了一部分开源的 NVIDIA 驱动组件如果你没有禁用 Nouveau安装器在加载新驱动时可能检测到模块冲突。虽然这更多影响“加载模块”而不是“编译模块”但为了干净起见最好先屏蔽它sudo tee /etc/modprobe.d/blacklist-nvidia.conf EOF blacklist nouveau options nouveau modeset0 EOF sudo update-initramfs -u然后重启系统让屏蔽生效再进行驱动安装。2.4 DKMS 与旧驱动残留的干扰如果你以前装过 NVIDIA 驱动系统里往往还留着旧的 DKMS 记录和/usr/src/nvidia-xxx源码目录。新安装器在注册模块时可能发现版本冲突或者因为旧目录权限不对、文件不完整而构建失败。建议在重新安装前先看一遍dkms status ls /usr/src | grep nvidia如果有明显的旧版本记录先移除sudo dkms remove -m nvidia -v 旧版本 --all如果之前是用.run方式安装的还可以用同版本的安装器自带的卸载功能sudo nvidia-uninstall清理完旧环境再开始新的安装成功率会高很多。别嫌这步麻烦我踩过很多次坑都是因为旧 DKMS 状态没清干净。2.5 两条安装路径的差异.run 与 apt很多人不理解为什么缓存在/var/log/nvidia-installer.log里的失败要用 apt 才顺利。其实.run和apt路径最后都要编译内核模块区别主要在版本匹配度上。.run是 NVIDIA 官方独立安装包它对内核版本、编译器版本的要求比较严格而 Ubuntu 官方仓库里的nvidia-driver-xxx是经过发行版测试的默认版本和当前内核、工具链往往更匹配所以失败率更低。但 apt 路径并不是万能药。如果你的内核头文件没装或者 Secure Boot 环境没处理apt 装驱动时同样会在 DKMS 构建阶段报错。所以建议先按照前几节的方法把环境清干净再看情况选哪条路。3. 实测有效的整套恢复流程3.1 阶段一采集现场信息很多人一看到报错就急着删掉.run文件重下其实最先要做的是收集现场信息。我每次处理这类问题都会先跑一遍以下命令uname -r gcc --version cat /proc/version mokutil --sb-state dkms status ls /usr/src | grep nvidia这组命令能告诉我当前内核版本、编译器版本、Secure Boot 状态、旧 DKMS 记录基本覆盖了 80% 的根因。如果是在图形界面环境下我会先切换到文本终端避免 X Server 干扰安装流程sudo systemctl isolate multi-user.target跑完之后想回到桌面用sudo systemctl isolate graphical.target即可。3.2 阶段二补齐头文件和构建工具链确认环境信息后第一件事是补齐基础依赖sudo apt update sudo apt install -y build-essential linux-headers-$(uname -r)build-essential包含 gcc、make 等基础工具linux-headers-$(uname -r)是精确对应当前内核版本的头文件包。如果系统提示该包找不到要认真看是不是有多个内核当前运行的内核是否有对应的仓库软件源。3.3 阶段三清理旧模块并禁用 Nouveau记得先清理旧驱动和 DKMS 记录再屏蔽 Nouveau。具体命令在前文已经列过这里再整理成一条完整序列sudo nvidia-uninstall # 如果之前用 .run 装过 sudo dkms remove -m nvidia -v 版本 --all sudo tee /etc/modprobe.d/blacklist-nvidia.conf EOF blacklist nouveau options nouveau modeset0 EOF sudo update-initramfs -u然后重启系统。重启后建议直接进入文本终端不要登录桌面。3.4 阶段四重新运行安装程序在干净环境下用如下方式运行安装包sudo sh ./NVIDIA-Linux-x86_64-550.144.03.run --dkms --uinone 21 | tee /tmp/nvidia-install.log--dkms参数表示注册到 DKMS以后内核升级时驱动可以自动重建--uinone减少图形界面的依赖避免额外交互21 | tee可以把完整输出保留下来方便失败后回溯。如果安装程序仍然报错不要慌直接看日志文件。重点搜索ERROR关键字通常能找到失败原因。3.5 阶段五换一条更省心的 apt 路线如果你对.run安装器实在没耐心或者试了几次还是卡在同一个位置我建议切换到 Ubuntu 官方仓库的 apt 安装方式sudo ubuntu-drivers devices这条命令会列出推荐的驱动版本。然后安装推荐项sudo apt install nvidia-driver-550装完后重启再运行nvidia-smi验证。使用 apt 的好处是版本经过发行版测试而且后续内核更新时 DKMS 会自动重新编译模块比自己手动下载.run省心很多。唯一前提是前面说的环境问题比如 headers、Secure Boot同样要先处理好。4. 安装日志应该怎么看4.1 从日志报错行推断根因/var/log/nvidia-installer.log是 NVIDIA 驱动安装的现场记录也是排查这个报错最重要的文件。日志内容会随着版本不同略有差异但常见的关键信息有一定的规律我整理成了一张速查表日志中的典型错误片段说明首选处理Unable to find the kernel source treeheaders 没装或 build 链接失效apt install linux-headers-$(uname -r)Kernel compiler ... is not the same as ...编译器版本与内核编译工具链不一致安装对应 gcc 版本并通过参数指定unrecognized command-line option -mcmodelkernelgcc 版本过旧或损坏升级或重装匹配 gccFailed to load the kernel module编译已通过但模块加载失败检查 Secure Boot、Nouveau 和模块签名ERROR: Failed to build ...紧跟着大量编译错误行一般是 API 或头文件不匹配先补 headers再尝试更换工具链版本日志里那些看起来复杂的长串 gcc 参数其实不用每个都看懂。你要做的是定位第一个ERROR或Error出现的位置看它前面几行是什么。如果是提示找不到头文件那就不是编译器的问题先补包如果是在编译某个.c文件时报语法错误那多半是工具链版本差异太大。4.2 日志里被很多人忽略的信息除了报错行日志头部还有一些值得注意的内容。比如日志第一段通常会写明运行用户、安装器版本、目标内核版本构建参数里会出现CCgcc-xx这个值可以和你gcc --version的输出对比如果日志里显示某个阶段的目录路径比如/usr/src/nvidia-550.144.03你可以手动进这个目录查看里面的.config或其他构建文件是否存在。还有一个很多人容易忽略的细节nvidia-installer.log每次运行都会被覆盖。如果你希望保留现场的报错日志可以在重试前先备份一份cp /var/log/nvidia-installer.log /tmp/nvidia-installer-fail.log这样即使后面重装把日志覆盖了你也能对比失败前后的差异。5. 常见问题速查与避坑清单5.1 从几个高频案例看问题定位根据我遇到和网友反馈的常见情况我整理了一张“现象→根因→处理”的对照表遇到类似问题时可以快速对照遇到的现象大概率原因处理方式第一次安装就卡在“Building kernel modules”headers 没装或 build 链接失效安装linux-headers-$(uname -r)apt upgrade之后重装驱动失败新内核 headers 缺失或旧模块残留按当前内核重新安装 headers清理 DKMS日志里全是 gcc 编译参数最后unrecognized...gcc 版本不匹配安装对应版本 gcc指定--kernel-cc安装过程顺利但重启后nvidia-smi报错Secure Boot 未处理或模块签名问题临时关闭 Secure Boot 或完成 MOK 注册用 apt 安装时 DKMS 构建失败同样多为 headers 缺失或不匹配先装 headers再重新安装驱动日志里提示模块被占用Nouveau 或其他旧模块未清理禁用 Nouveau清理旧驱动然后重启这张表不能覆盖所有情况但覆盖了绝大多数“Building kernel modules”失败的核心场景。5.2 我踩过多次坑之后总结的四个“不要”第一不要反复重下同一个.run文件。驱动包本身出问题的概率极低重复下载纯属浪费时间而且如果你没有备份还会把原本有分析价值的日志覆盖掉。出现报错后先看/var/log/nvidia-installer.log这是最快的路。第二不要跳过 headers 安装直接重试。很多人在缺 headers 的情况下反复重装结果每次都卡在同一句“Building kernel modules”。这不是玄学是因为构建阶段缺少必要源码安装器根本走不下去。补上 headers问题立刻消失。第三不要在图形界面环境下反复尝试。X Server 在运行的时候NVIDIA 安装器可能因为检测到显示服务而做出额外处理或者模块加载阶段被占用。切到文本终端让安装器在一个相对干净的环境里运行能排除很多干扰因素。第四不要随手乱删/usr/src下的目录。DKMS 源码目录如果删得不干净可能影响新驱动注册。正确的做法是先用dkms status确认旧版本号再用dkms remove清理。如果已经乱删了可以重新安装对应 headers 和驱动包引导 DKMS 重新生成。我在实际处理这类问题时最后往往会先用nvidia-smi做一个快速验证如果能看到显卡型号、驱动版本和显存信息说明模块已经成功加载。这时候再回到图形界面跑一次压力测试或者glxinfo确认渲染正常基本就算收工了。如果你现在正卡在同一个报错上我的建议是先看日志再补 headers清理旧模块最后切到命令行重装。大部分情况下十分钟内就能解决。别在没分析原因之前对着.run反复重装那才是真的浪费时间。