最近遇到一台老掉牙的国产桌面 Linux 系统,具体版本对应的是麒麟 V10 的 x86_64 桌面版,要装一块新出不久的 RTX 5090 显卡。硬件插上去后一切看着都正常:风扇在转,系统也能顺利进桌面,但驱动一装完,nvidia-smi直接给我一句 “No devices were found”。
这句话的迷惑性非常大。因为lspci里明明能看到显卡,设备管理器里也不是空白,可驱动层面就像完全没意识到这张卡存在一样。我前后折腾了两天,重装驱动、换驱动版本、改内核启动参数、换插槽,全都试过,最后才把问题闭环。这篇文章就是完整记录这次排查和安装过程,适合正在给旧版本 Linux 发行版配新显卡、又对“找不到硬件”错误一头雾水的朋友。文章会讲清楚报错到底是谁发出来的、怎么区分硬件和驱动问题、安装驱动的正确顺序,以及我踩过的几个大坑。如果你也在麒麟或类似环境上装新 NVIDIA 显卡,这篇可以直接照着做。
1. 项目核心背景:为什么新显卡在旧发行版上会“失踪”
1.1 “No devices were found”到底是谁在说话
很多人第一次看到这条报错,第一反应是显卡坏了或者没插紧。我一开始也这么想,还特意换了供电线反复插拔,结果错误依旧。真正把问题定位清楚后我才发现,这句话根本不是硬件层发出的,而是显卡内核驱动模块在初始化阶段主动选择的“放弃”。
Linux 下安装 NVIDIA 闭源驱动,本质上是把一个叫nvidia.ko的内核模块加载进系统。这个模块加载后,会去遍历 PCIe 总线上的每一个设备,读取它们的厂商 ID 和设备 ID,再和自己驱动包里支持的那张 ID 列表做比对。如果找了一圈都没找到匹配项,模块就会在内核日志里写一条类似nv: Error: No devices were found的消息,然后把自己标记为“探测失败”。等用户再执行nvidia-smi的时候,它自然就只能说一句“找不到 NVIDIA 显卡”。
这一点是整个排错的关键。它意味着问题被分成两条完全独立的路径:一条是硬件层的问题,比如显卡根本没有被 PCIe 枚举到、BIOS 设置把设备屏蔽了、供电不足或者插槽带宽受限;另一条是驱动层的问题,比如内核版本过旧、驱动版本太老、显卡的设备 ID 不在支持列表里、又或者开源驱动nouveau已经把设备占用了,专用驱动模块不好意思再去争抢。这两类问题的处理方式南辕北辙,如果分不清,就会一直在原地打转。
1.2 新显卡与保守内核之间的鸿沟
RTX 5090 这种新架构显卡,对内核的要求比以往更高。它需要的不仅仅是“能点亮”那么简单,还涉及 DRM/KMS 显示接口、PCIe BAR 窗口的重新映射、显存管理的新接口等一堆底层能力。相比之下,麒麟 V10 这种商业桌面系统的默认内核,通常版本比较保守,很多还是几年前的内核分支。在这个旧内核上,新显卡可能连 VBIOS 的初始化协议都跑不通,自然就被驱动跳过了。
另外一个容易被忽略的细节是编译环境。官方驱动安装包在安装时需要把内核模块源码和当前内核头文件编译匹配。新显卡驱动的源码会引用较新版内核里才有的函数或数据结构,如果内核头文件太老,编译会直接报错。于是你经常会看到“unsupported kernel”或者一堆莫名其妙的编译错误。就算有人用--force之类的参数硬装上去,模块加载时还是可能因为符号缺失直接失败。
还有个很多人忽视的点,是平台相关的 PCIe 配置。新显卡的内存寻址范围很大,如果 BIOS 没打开“Above 4G Decoding”和“Resizable BAR”这两个选项,BAR 窗口会被压在传统 32 位地址空间里,驱动初始化时极容易出现资源分配失败。我后来在 BIOS 里把这两个选项打开,算是解决了最后一层“看不见”的问题。所以这次排错其实是软硬结合,驱动层解决版本兼容,硬件层解决寻址窗口和启动模式。
2. 动手前先做诊断:把问题定位到三层
2.1 用 lspci 确认硬件枚举
正式安装驱动前,我先把工具链跑了一遍。第一步是确认内核到底有没有发现这张显卡,命令很简单:
lspci -nnk | grep -i -A3 -B3 'nvidia\|vga'如果输出里能看到类似VGA compatible controller ... NVIDIA ...的字段,说明 PCIe 枚举是正常的,硬件已经被内核承认,问题大概率落在驱动和模块加载上。如果lspci里压根找不到这张卡,那基本不用碰驱动,直接去查物理连接、供电线、插槽和 BIOS 拆分设置。
lspci -k还能告诉你当前设备绑定了哪个驱动。这一步信息量很大。因为同一时刻一张显卡只能被一个内核驱动管理。如果nouveau已经先插了一脚,后面安装的官方驱动就会认为设备已经有人接管,探测阶段直接不参与,举出来的结果还是那句“没有设备”。所以我在确认lspci能看到设备后,紧接着用lspci -k查看当前驱动归属,这一步帮我省了很多弯路。
2.2 用 dmesg 和 lsmod 判断驱动状态
确认了硬件枚举,接下来要看驱动模块的实际加载情况。我习惯用两条命令一起看:
lsmod | grep -i -E 'nvidia|nouveau' dmesg | tail -200 | grep -i -E 'nvidia|nouveau|pci'lsmod能看出哪些显卡相关模块已经加载,dmesg则能给出更详细的初始化日志。如果看到类似nvidia: probe of 0000:01:00.0 failed with error -12,代表模块其实已经找到了设备,但是资源申请失败,这种情况要优先怀疑 BAR 窗口不够或中断配置问题,BISO 设置优先级最高。如果看到nv: Error: No devices were found,才是真正意义上的“设备 ID 不匹配”或“nouveau 占坑”。如果看到 nouveau 初始化成功的日志,那就很明确了:把 nouveau 从启动阶段彻底拆掉再说。
把这三个结果和lspci串联起来,基本就能判断“硬件三层层”中的哪一层出了问题。硬件枚举、驱动归属、初始化结果,三者各查一遍,基本上不会再有方向性错误。
2.3 一张可以直接照抄的排查决策表
为了后面少折腾,我把这次遇到的情况扩展成了一张诊断表。读者可以按当前现象直接跳到对应解法:
| lspci 结果 | lsmod 显示 | dmesg 报错 | 优先处理动作 |
|---|---|---|---|
| 能看到显卡 | nouveau 加载中 | 无 nvidia 报错 | 禁用 nouveau 并重建 initramfs |
| 能看到显卡 | 无任何显卡驱动 | nvidia: No devices were found | 升级内核或换用更新的官方驱动 |
| 能看到显卡 | nvidia 加载中 | error -12 / resource busy | BIOS 开启 Above 4G 解码、ReBAR |
| 看不到显卡 | 无 | 没有设备被枚举 | 检查插槽、供电、PCIe 拆分设置 |
这张表没法覆盖所有机器,但已经把最典型的情况列出来了。我后来在正式安装时,每一步都拿这张表对一遍,确认是否真的到位。与其反复试装碰运气,不如把现象和动作对应清楚,效率高得多。
3. 安装实战:从准备到验证的完整流程
3.1 先统一内核版本和工具链
我在前面诊断时发现当前内核版本太旧了,所以安装驱动前先做的一件事就是升级内核。麒麟 V10 的软件仓库一般会提供较新的内核包,我用包管理器先搜索一下:
dnf list available kernel*如果你的桌面版是基于 Debian/Ubuntu 体系的,对应的命令是apt list kernel-image-*。我这边属于 RPM 系仓库,最后在源里找到了一个能支持新显卡架构的内核。安装新内核包之后,记得同步安装对应的头文件包:
sudo dnf install kernel-headers kernel-devel gcc make dkms这里必须强调一个最容易踩的坑:内核头和内核本体版本要完全一一对应。我曾见过有人装了新内核,但头文件包的版本还是旧的,结果编译驱动时一直报找不到generated/autoconf.h。这种错误让人摸不着头脑,原因其实就是/usr/src/kernels/目录下没有与uname -r匹配的头文件树。最稳的做法是安装完直接列出目录确认:
ls /usr/src/kernels/$(uname -r)gcc 版本也要尽量和内核编译时用的版本一致。差距太大时,官方驱动安装脚本会在cc version check这一步报警。这个检查可以强行跳过,但我不建议一上来就这么干,能对齐还是先对齐,因为内核模块编译时的 ABI 兼容性和编译器版本关系很大。
3.2 关掉 nouveau 的正确姿势与顺序
禁用 nouveau 这件事,看起来只需要写一个配置文件,实际操作里却藏着很大的变数。因为 nouveau 很可能在系统启动早期就通过 initramfs 被加载了。如果只改/etc/modprobe.d/blacklist-nouveau.conf而不重新生成 initramfs,重启后老旧的启动镜像照样会把 nouveau 拉起来,等于“白禁用”。
正确的做法是,先创建黑名单文件:
# /etc/modprobe.d/blacklist-nouveau.conf blacklist nouveau options nouveau modeset=0然后重新生成 initramfs 并重启:
sudo dracut -f sudo reboot如果系统用的是mkinitrd,把dracut -f替换成sudo mkinitrd -f就好。重启后第一件事就是确认 nouveau 是否真的没了:
lsmod | grep nouveau没有输出,才算拆干净。这个步骤的顺序非常重要,一定要在安装专有驱动之前完成,而不是装完再补救。因为 nouveau 一旦在运行时绑定了显卡,官方驱动安装脚本要么中止执行,要么装完后模块加载失败。我刚开始就是没重建 initramfs,导致后面的安装过程反复卡住。
3.3 选择正确的驱动包和安装参数
驱动包的选择同样是关键一步。新显卡必须用新驱动,这一点没有商量余地。我在官方下载页面按照当前内核架构取了一个较新的企业版驱动包,下载后先给它加执行权限:
chmod +x NVIDIA-Linux-x86_64-xxx.run然后切到纯文本模式安装。图形界面环境下直接跑安装脚本,容易在改写 X 配置时和桌面会话冲突。我的做法是临时切到 multi-user 目标:
sudo systemctl isolate multi-user.target接着运行安装脚本:
sudo ./NVIDIA-Linux-x86_64-xxx.run --no-x-check --no-nouveau-check--no-x-check是为了在关闭图形环境后不被安装脚本拦下来;--no-nouveau-check是让脚本忽略 nouveau 残留配置的检查。如果出现 gcc 版本不一致的报错,可以再加--no-cc-version-check强制跳过,但这是最后手段,前提是你能确认编译时不会出根本性错误。
安装过程中,脚本会问你几个问题:是否安装 32 位兼容库、是否更新 X 配置、是否用 DKMS 注册模块。我按推荐配置全部选“是”,并且重点看了安装日志里有没有 DKMS 注册成功的信息。DKMS 注册成功意味着以后内核小版本升级时,驱动模块能自动重建,不用每次手动重装。如果日志里没有出现 DKMS 相关条目,基本可以断定内核头文件不匹配,需要回到第 3.1 节重新检查。
3.4 安装后的模块加载和桌面验证
驱动装完、重启之后,我习惯用一组命令把结果一次验证完:
nvidia-smi lsmod | grep nvidia dmesg | grep -i nvidia | head -50nvidia-smi能正常显示 GPU 型号、驱动版本和显存容量,说明模块加载和设备识别都成功了。lsmod里如果能看到nvidia_drm、nvidia_modeset、nvidia_uvm这几个模块,才算完整。缺少nvidia_uvm时,CUDA 程序会给出“设备初始化失败”之类的误导性错误,需要特别注意。
桌面显示不正常或者黑屏时,检查一下 X 服务配置文件是否生成:
sudo nvidia-xconfig这条命令会生成/etc/X11/xorg.conf,把 NVIDIA 设备作为主显示设备绑定进去。麒麟 V10 默认大多使用 X11 桌面会话,所以配置好 xorg.conf 后,重启图形会话基本就能点亮。如果使用的是 Wayland 话,还需要额外的环境变量配置,不过在实际使用里,基于这套系统跑桌面和训练任务,X11 协议更成熟稳定,也是我更推荐的方案。
4. 常见问题与排查技巧实录
4.1 按现象定位原因的速查表
在实际调试过程中,我反复遇到过一堆“表面上看不一样,本质上却相通”的现象。这里整理成一张速查表,读者可以按自己看到的报错直接跳转:
| 现象 | 最可能原因 | 解决方向 |
|---|---|---|
nvidia-smi报找不到设备,但 lspci 能看到 | 驱动不识别新设备 ID | 升级驱动包或内核版本 |
No devices were found且模块日志为空 | 模块未加载成功 | 检查头文件、DKMS 注册 |
| 安装脚本报 kernel 版本不支持 | 内核太旧 | 升级内核并同步头文件 |
| 安装后重启黑屏,只剩命令行 | nouveau 未禁用干净 | 重建 initramfs 并验证 |
| 模块加载报 error -12 | BAR 窗口不足 | BIOS 开启 Above 4G / ReBAR |
| 桌面能进但分辨率不对 | 尚未生成 xorg.conf | 执行 nvidia-xconfig |
| 每次内核升级后驱动失效 | DKMS 未生效 | 检查 dkms status 并重装模块 |
这张表我建议收藏。Linux 下装闭源驱动的故障,大部分都能在上面找到答案。我自己的经验是先看现象对应关系,再翻日志,能省很多时间。
4.2 Secure Boot 和 BIOS 里的几个关键开关
如果机器开了 Secure Boot,那没给 NVIDIA 内核模块签名的话,系统启动阶段就会拒绝加载它。表现出来的症状还是“No devices were found”。处理方式有三条:一是直接在 BIOS 里关闭 Secure Boot,最省事但安全策略会弱一点;二是启用 MOK 工具给模块签名,过程麻烦,而且每次内核更新后都要重新签一次;三是等发行版发布已经签好名的驱动包,但新显卡刚出来时这种包往往跟不上。
BIOS 里另一个关键点是 PCIe 相关设置。请在主板设置里搜索 “Above 4G Decoding” 和 “Resizable BAR / ReBAR”,尽量都打开。RTX 5090 的显存寻址范围非常大,如果不开启这两个选项,BAR 地址窗口会被限制在 4G 以下,驱动初始化时极易碰到resource busy或error -12这类资源冲突错误。另外,PCIe 拆分模式也要看一眼,如果把显卡插在拆分后的 x4 或 x8 槽上,带宽不足不说,部分卡初始化还会不稳定。最稳妥的位置是第一条 x16 全长槽。
4.3 我实际踩过的几个大坑
先说第一个坑:自以为禁用了 nouveau。我一开始只写了黑名单文件,没重新生成 initramfs,重启后 nouveau 依然被旧镜像加载。直到安装驱动后模块始终不工作,才反应过来要重新生成启动镜像。这个错误在日志里并不显眼,但影响非常致命。
第二个坑是内核头文件版本不匹配。当时驱动安装脚本没有立刻报错,而是在编译后期才失败。我以为下载的驱动包损坏了,连续下载好几次都没解决问题。后来才想到查看/usr/src/kernels/目录是否存在与当前内核对应版本的子目录。这个问题往往被“应该安装了吧”的错觉掩盖,实际排查起来非常浪费时间。
第三个坑是图形会话和命令行会话混用。我第一次安装时开着桌面会话直接跑脚本,结果装到一半桌面崩溃,X 配置也被改得乱七八糟。后来切到纯文本模式安装,整个过程没有再出问题。只要是有图形界面的系统,安装闭源驱动前先切到 multi-user 模式,等于把可能干扰系统日志的变量降到最低。
第四个坑则是内核升级后启动项没切换。我在新内核没有进入 GRUB 默认启动项的情况下直接重启,系统回到了旧内核。旧内核里没有新模块,驱动当然不会加载,我还以为安装失败,差点打算重装系统。解决办法是在重启前运行:
sudo grub2-set-default 0或者启动菜单里手动选择新内核项。内核选对,模块加载才会正常,这个细节很容易被忽略。
5. 关于版本锁定的补充建议
5.1 记下一份“内核-驱动-工具链”版本清单
装好驱动、验证没问题之后,我建议立刻把当前内核版本、驱动版本、gcc 版本、dkms 状态这四件事记录下来。可以放在自己的备忘笔记里,内容大致是:
uname -r nvidia-smi | head -3 gcc --version dkms status为什么要做这件事?因为国产 Linux 桌面发行版的更新策略通常比较保守,仓库里某个软件包可能很久不更新,但这不代表内核和驱动之间的兼容关系保持不变。一旦隔了几个月后偶然一次升级把内核更新了,而 DKMS 没能自动重建模块,驱动就可能在重启后失效。此时如果提前存好版本清单,回退起来会非常方便。
5.2 升级前先备份可用模块状态
我个人的做法是,在系统状态正常时导出当前模块状态到一个文件:
nvidia-smi > /tmp/nvidia-smi-backup.txt dkms status > /tmp/dkms-status-backup.txt这样即使版本升级后模块编译失败,也能对照备份文件快速判断问题在哪里。国产 Linux 系统的新硬件支持往往滞后,我们总不能等发行版官方更新,因此自己维护一个可用的驱动/内核组合是长期稳定的前提。
这次装完 RTX 5090 后,我心里最大的体会是:遇到 “No devices were found” 这类报错,第一反应别是怀疑硬件损坏,更别急着反复重装驱动。先确认 lspci 能否看到设备,再检查 nouveau 是否占用,接着确认内核版本和驱动版本是否匹配,最后再动 BIOS。按照这个顺序去排查,绝大部分问题都能在三十分钟内定位出来。希望这篇记录能给你省下我当初绕的一大圈弯路。