news 2026/10/21 14:18:49

Ubuntu安装NVIDIA驱动报错Building kernel modules的排查与解决

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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-cc=gcc-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 modeset=0 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 modeset=0 EOF sudo update-initramfs -u

然后重启系统。重启后建议直接进入文本终端,不要登录桌面。

3.4 阶段四:重新运行安装程序

在干净环境下,用如下方式运行安装包:

sudo sh ./NVIDIA-Linux-x86_64-550.144.03.run --dkms --ui=none 2>&1 | tee /tmp/nvidia-install.log

--dkms参数表示注册到 DKMS,以后内核升级时驱动可以自动重建;--ui=none减少图形界面的依赖,避免额外交互;2>&1 | 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 '-mcmodel=kernel'gcc 版本过旧或损坏升级或重装匹配 gcc
Failed to load the kernel module编译已通过但模块加载失败检查 Secure Boot、Nouveau 和模块签名
ERROR: Failed to build ...紧跟着大量编译错误行一般是 API 或头文件不匹配先补 headers,再尝试更换工具链版本

日志里那些看起来复杂的长串 gcc 参数其实不用每个都看懂。你要做的是定位第一个ERROR或Error出现的位置,看它前面几行是什么。如果是提示找不到头文件,那就不是编译器的问题,先补包;如果是在编译某个.c文件时报语法错误,那多半是工具链版本差异太大。

4.2 日志里被很多人忽略的信息

除了报错行,日志头部还有一些值得注意的内容。比如:

  • 日志第一段通常会写明运行用户、安装器版本、目标内核版本;
  • 构建参数里会出现CC=gcc-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反复重装,那才是真的浪费时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 14:18:47

pandas数据合并:concat、merge、join的区别与实战用法详解

先别急着背文档&#xff0c;我直接说结论&#xff1a;很多人学 pandas 学到一半&#xff0c;碰到 concat、merge、join 这三个东西就开始晕&#xff0c;因为光看名字都像“合并数据”&#xff0c;但实际用起来完全不是一回事。我最初也被坑过好多次&#xff0c;在项目里写错函数…

作者头像 李华
网站建设 2026/10/21 14:18:47

STM32+红外模块DIY空调遥控器:格力协议逆向与PWM载波实现

简介&#xff1a;这份资源面向嵌入式开发初学者与智能家居爱好者&#xff0c;提供基于STM32微控制器驱动红外模块控制格力空调的完整工程实践。内容围绕STM32的GPIO推挽输出、定时器PWM波形生成、NEC红外编码与解码、格力遥控协议适配等核心知识点展开&#xff0c;帮助读者理解…

作者头像 李华
网站建设 2026/10/21 14:18:13

IPv6编址实战指南:地址结构、子网划分与配置避坑

1. 为什么IPv6编址值得你花时间搞明白如果你手里管着几台服务器、几组交换机&#xff0c;或者正在准备网络相关的认证考试&#xff0c;那IPv6编址这件事基本绕不过去。我最早接触IPv6是在一次内部网络改造项目里&#xff0c;当时第一反应是“地址变长了而已”&#xff0c;结果真…

作者头像 李华
网站建设 2026/10/21 14:17:48

五子棋AI的α-β剪枝:从极大极小值到高效搜索的Python实现

简介&#xff1a;这是一份面向Python初学者与棋类算法爱好者的五子棋人机对战实现方案&#xff0c;核心解决电脑走步的智能选取问题。程序采用极大极小值搜索&#xff0c;并引入α-β剪枝提升搜索效率&#xff0c;同时实现候选落点生成与评估函数设计&#xff0c;评估函数直接决…

作者头像 李华
网站建设 2026/10/21 14:15:48

MD5+UTF-8 API签名机制详解:从原理到代码实现

做接口开发的这几年&#xff0c;我最常被问到的不是“怎么写接口”&#xff0c;而是“为什么我调接口老报签名错误”。就拿这个api请求加密&#xff08;MD5UTF-8&#xff09;来说&#xff0c;听起来是三个词拼在一起&#xff0c;实际落地时涉及编码规则、参数排序、密钥约定、时…

作者头像 李华
网站建设 2026/10/21 14:15:46

Linux磁盘配额实战:ext4与XFS从配置到排查

接手一台多用户服务器&#xff0c;最先遇到的头疼问题十有八九是磁盘被某个人塞满。前阵子帮朋友整理一台开发测试机&#xff0c;/home 分区 500G&#xff0c;结果某位同事把打包好的日志、镜像一股脑往里丢&#xff0c;三天不到就爆了&#xff0c;其他账号开始报 No space lef…

作者头像 李华