装显卡驱动和CUDA,这个主题听起来挺老生常谈,但我在实际接触中,发现每年都有大量新入坑的人卡在这一步,而且卡的方式千奇百怪。有人装完驱动黑屏,有人循环登录,有人好不容易装上CUDA,结果跟框架版本对不上,还有人升级一次内核之后,nvidia-smi直接原地消失。这篇文章我打算把所有关键细节都摊开讲,围绕Ubuntu系统上安装N卡驱动和CUDA这件事,尽量把原理、步骤和坑都讲透,覆盖从下载到验证的完整链路。
这篇文章适合这几类人看:准备在Ubuntu上搭深度学习环境的人、被黑屏和循环登录折磨到想重装系统的人、以及在驱动和CUDA版本之间反复横跳的新手。我会尽量用“为什么要这样做”的视角来讲,而不是简单甩给你一串命令。
1. 安装前必读:版本、环境与整体思路
1.1 先把版本关系捋清楚
很多人装驱动失败,根源只有一个:没搞明白驱动、CUDA和系统内核三者之间的关系。
先说驱动与CUDA的关系。N卡驱动是“向下兼容”的,也就是说,新版驱动通常支持所有旧版CUDA,但旧版驱动不一定支持新版CUDA。每个CUDA版本都有一个最低驱动版本要求,这个表格在NVIDIA官网能查到。举个实际例子,CUDA 12.x需要驱动版本至少在530以上,而你现在用的是550或者560的驱动,那跑CUDA 12.x完全没问题。反过来如果你只装了520的驱动,却想用CUDA 12.2,那就装不上了。
再一个容易混淆的点是nvidia-smi显示的CUDA版本和nvcc -V显示的CUDA版本。nvidia-smi右上角显示的CUDA Version是指当前驱动支持的最高CUDA版本,它表示你最多可以装到这个版本的CUDA;而nvcc -V显示的是你实际安装的CUDA Toolkit版本。两者不一定要完全一致,只要“实际安装的CUDA版本 <= 驱动支持的最高版本”就行。
系统内核同样重要。Ubuntu的驱动安装包基本都是针对特定内核版本编译的,如果你升级内核后驱动没有跟着重新编译,就会莫名其妙“掉驱动”。这也是为什么我要在后面强调dkms的重要性。
1.2 工具与准备工作清单
在正式安装之前,我建议你花五分钟把环境信息看清楚。打开终端,依次执行以下命令:
# 查看系统版本,比如Ubuntu 22.04 LTS lsb_release -a # 查看内核版本 uname -r # 查看显卡型号,确认是N卡且型号正确 lspci | grep -i nvidia # 查看是否有gcc编译器,后面编译驱动需要 gcc --version如果是刚装好的系统,gcc大概率没有安装,最好先补上基础编译工具。
sudo apt update sudo apt install build-essential dkmsbuild-essential包含编译器和基础库,dkms则是动态内核模块支持工具。dkms能帮你在内核升级后自动重新编译驱动模块,这个后面详细说。
另外强烈建议在动手前开启系统自带的“创建快照”功能,或者用timeshift之类的工具做一个系统备份。装驱动最坏的结果就是系统反复重启不进桌面,有备份能让你随时回到安全状态继续折腾,没有备份你可能就得抱着启动U盘重装系统了。
下载驱动时,先去NVIDIA官网的“驱动下载”页面,选择你的显卡型号和Linux 64位系统,会得到一个类似NVIDIA-Linux-x86_64-550.xx.run的文件。注意不要下载成Windows版。CUDA Toolkit从官网选择Linux对应的版本下载即可,建议选择.run格式的本地安装包,而不是Debian网络安装包,原因后面讲。
1.3 三种主流安装方式如何选
N卡的驱动安装方式大体有三种,我在这里把它们放在一起对比一下,方便你根据自己情况选:
| 安装方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| apt仓库安装 | 命令简单,跟系统集成好,卸载方便 | 版本通常不是最新的;可能被系统更新影响 | 只跑稳定应用、不想折腾的人 |
| 官方.run文件 | 最新版本,可控性强,安装参数灵活 | 需要手动管理;内核升级后可能掉驱动 | 追求新版本、自己解决问题的人 |
| CUDA Toolkit捆绑安装 | 驱动和CUDA一次性装好 | 容易重复装驱动导致冲突 | 全新环境、最省事的情况 |
我的建议是:第一次安装,优先尝试用官方.run文件单独装驱动,再用.run方式单独装CUDA。虽然步骤多一些,但每一步发生了什么你都清楚,出问题时排查起来也简单。如果只是为了用某个固定版本的驱动,也不想处理奇怪的问题,那么apt仓库方式肯定更省心。两种方式我下面都会给出详细步骤。
2. 第一步:安装N卡驱动,两个最稳的方案
2.1 推荐方案:官方.run文件手动安装
手动安装标准的流程分为四个阶段:屏蔽开源驱动、进入纯命令行模式、执行安装程序、重启验证。
先解释为什么需要屏蔽开源驱动。Ubuntu内核自带一个叫nouveau的开源驱动模块,它会在系统启动时自动加载,占住显卡设备。如果NVIDIA官方驱动要安装,就会和nouveau产生冲突,导致模块加载失败、黑屏或者循环登录。所以要在驱动安装前把它拉黑。
创建一个拉黑配置文件:
sudo vim /etc/modprobe.d/blacklist-nouveau.conf写入下面的内容:
blacklist nouveau options nouveau modeset=0然后更新内核引导配置并重启:
sudo update-initramfs -u sudo reboot重启之后验证一下nouveau确实被禁掉了:
lsmod | grep nouveau如果没有任何输出,说明拉黑成功。这一步别偷懒,我见过太多人跳过这步直接装驱动,然后百思不得其解为什么启动进不了桌面。
接着进入纯命令行模式。在桌面环境下按Ctrl + Alt + F2,会切换到全黑的虚拟终端界面,用你的用户名登录。执行下面命令关闭图形桌面服务:
# Ubuntu 22.04以后一般是gdm3,如果用的是lightdm则换成lightdm sudo service gdm3 stop注意,如果你的显卡原本是正常工作的,关闭图形服务后屏幕会进入类似纯文本终端的状态,这是正常的。从这里执行驱动安装程序可以避开图形环境对安装过程的干扰。
先确认你下载的.run驱动文件有执行权限:
chmod +x NVIDIA-Linux-x86_64-550.xx.run然后执行安装:
sudo ./NVIDIA-Linux-x86_64-550.xx.run --no-opengl-files --no-x-check --no-nouveau-check --dkms标志参数解释一下:
--no-opengl-files表示不安装OpenGL相关文件。虽然NVIDIA驱动自带的OpenGL库在某些场景下是必要的,但对普通用户和深度学习场景来说,跳过OpenGL文件可以避免很多桌面环境下的兼容性问题,特别是如果你不打算用这台机器玩游戏或跑桌面级图形加速。--no-x-check意思是安装时不检查X服务是否在运行。因为我们手动关了图形服务,这个参数告诉安装程序不用纠结当前有没有图形环境。--no-nouveau-check跳过nouveau检测。--dkms让驱动以dkms方式注册,这样以后升级内核时驱动模块能自动重新编译。
安装过程中会问你一些问题,比如是否接受许可证、是否需要注册内核模块等,基本一路选“Yes”或“OK”即可。有些版本会问是否安装32位兼容库,如果你主要的场景是深度学习,其实不是必须的,可以选“No”加快安装速度。
安装完成后重启系统:
sudo reboot重启完打开终端,执行:
nvidia-smi如果能看到类似下面这样的一张表格,包含驱动版本、GPU利用率、显存使用情况,那驱动就算装成功了。
+---------------------------------------------------------------------------------------+ | NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 | |-----------------------------------------+----------------------+----------------------+2.2 省心方案:apt仓库一键安装
如果你想用更省心的方式,Ubuntu的apt仓库里也维护了NVIDIA驱动。优点是不需要关图形界面、不需要手动下载run文件,直接一条命令搞定:
# 查看系统推荐哪些驱动版本 ubuntu-drivers devices执行后,它会列出你的显卡支持的驱动列表,并在某个版本后面标记recommended。然后直接一键安装:
sudo ubuntu-drivers autoinstall这个命令会自动安装推荐版本的驱动。如果想手动指定版本:
sudo apt install nvidia-driver-550装完同样重启,然后nvidia-smi验证。
apt方式的缺点也比较明显:第一,仓库里的驱动版本往往不是最新,发布节奏比NVIDIA官方慢不少;第二,如果你以后手动安装CUDA Toolkit时选择“安装驱动”,可能会把apt装的驱动覆盖成另一个版本,导致版本混乱。如果你确定自己以后只通过apt管理驱动,那就保持这种方式。如果以后打算频繁更新CUDA版本,建议还是用.run方式。
2.3 安装后的验证与驱动状态解读
nvidia-smi输出的信息量很大,这里简单说下怎么看:
- 上方的
Driver Version就是驱动版本号,CUDA Version表示当前驱动能支持的最高CUDA版本。 - 下方的表格显示每个GPU的实时状态,包括温度、显存使用率、功耗、运行中的进程等。
- 如果GPU被某些进程占用了,在进程列表能看到PID和占用显存大小。
如果在nvidia-smi里能看到显卡,但运行nvidia-settings报错,或者图形界面有问题,绝大多数情况是因为OpenGL相关的文件被跳装了,或者和显卡桌面会话冲突。对纯粹跑深度学习的人来说,这并不影响实际使用。如果很在意图形界面性能,可能要回到图形模式下重新执行一次不带--no-opengl-files的安装,但这属于进阶调优了。
另外提醒一点:如果你的机器是笔记本双显卡(Intel核显+N卡独显),装完闭源驱动后可能需要用prime-select切换显卡模式。比如:
# 查看当前模式 prime-select query # 切换到N卡独显 sudo prime-select nvidia这个命令和桌面环境集成度有关,有的人需要,有的人不需要。遇到相关问题时可以朝这个方向查。
3. 第二步:安装CUDA Toolkit与环境配置
3.1 下载CUDA Toolkit与安装参数解析
驱动装好之后,接着装CUDA Toolkit。先去NVIDIA官网的CUDA下载页面,选择操作系统、架构、发行版本、安装方式。我建议选择runfile (local)格式,这样能用本地安装包离线安装,可控性最好。
它会给你类似下面这样的下载链接和安装命令:
wget https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_550.54.15_linux.run下载完成后执行安装:
sudo sh cuda_12.4.0_550.54.15_linux.run注意,执行后它首先会进入一个文本图形界面(类似ncurses菜单),里面有一堆组件选项。这里有个极其关键的步骤:如果你已经手动装好了驱动,在组件列表里一定要取消勾选“Driver”。怎么操作?在Driver那一项上按回车,把[X]改成[ ]。其余组件比如CUDA Toolkit、Samples、Documentation可以都保留,然后选“Install”开始安装。
为什么要取消Driver?因为官方run包里的驱动版本可能和你已经安装的不完全一致,重复安装可能覆盖已有驱动或者版本冲突。我在实际安装中见过好几次有人没取消Driver,装完CUDA之后驱动直接出问题,回去查才发现驱动被覆盖了,版本还不匹配。多花这一步操作,能少踩一个大坑。
如果你从一开始就打算让CUDA安装包顺便把驱动也装了,那可以不加任何参数直接装,但前提是系统从没装过NVIDIA驱动,并且你也想省事。这种适合全新系统。
安装过程中它会提示安装路径,一般是/usr/local/cuda-X.Y,同时会帮你建立一个/usr/local/cuda软链接指向这个版本目录。这个软链接在版本切换中很有用,后面细说。
3.2 环境变量配置与软链接的作用
安装完成后,把CUDA的bin和lib64目录加入环境变量。编辑用户配置文件:
vim ~/.bashrc在文件末尾追加:
export PATH=/usr/local/cuda/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH然后让配置生效:
source ~/.bashrc为什么这里的路径写的是/usr/local/cuda而不是/usr/local/cuda-12.4?因为/usr/local/cuda是一个软链接,始终指向当前默认要使用的CUDA版本。这样你如果以后想切换到另一个版本,只需要改变软链接指向,环境变量不用动。
LD_LIBRARY_PATH是运行时库搜索路径。CUDA程序在编译和运行时要找libcudart.so、libcublas.so这一类动态库,不设置这个环境变量的话,即使编译过了,运行程序时也会报error while loading shared libraries的错误。这类报错的排查方式无非是确认lib64下确实有对应的.so文件,以及确认这个路径确实在LD_LIBRARY_PATH里。
3.3 多版本CUDA共存与切换
实际项目中经常出现这种情况:代码框架依赖的是CUDA 11.8,但新训练的框架非要CUDA 12.x。于是很多人被逼着学会管理CUDA多版本。多版本共存完全可行,因为每个CUDA Toolkit安装后都在独立目录下,互不干扰。
关键是管理好/usr/local/cuda这个软链接。比如我装了CUDA 11.8和CUDA 12.4,需要切到11.8时:
sudo rm -rf /usr/local/cuda sudo ln -s /usr/local/cuda-11.8 /usr/local/cuda切回12.4就把目标路径换成/usr/local/cuda-12.4。切换后重新打开一个终端或重新source ~/.bashrc,nvcc -V就会显示对应版本。
如果你想更规范一点,也可以用update-alternatives管理:
sudo update-alternatives --config cuda但说实话,手动改软链接对个人开发机来说已经足够简单粗暴好用了。唯一要注意的是,环境变量PATH和LD_LIBRARY_PATH里的路径写的是/usr/local/cuda,所以它们始终跟随软链接变化,不需要改配置文件。这个设计是为了省事,也是很多老手管理多版本CUDA的通用做法。
3.4 验证CUDA:从nvcc到deviceQuery再到写段代码
装完之后先验证工具链:
nvcc -V如果能看到类似Cuda compilation tools, release 12.4, V12.4.131这样的输出,说明编译器装好了。
接着编译并运行NVIDIA官方自带的示例程序:
# 进入示例目录(在安装目录下) cd /usr/local/cuda/samples/1_Utilities/deviceQuery # 编译 sudo make # 运行 ./deviceQuery如果最后输出Result = PASS,说明驱动、CUDA Toolkit、显卡之间的通路完全正常。
但我还是建议你亲手写一段最简单的CUDA代码编译运行一下,这样能完整走一遍“编辑-编译-运行”的流程,对后续理解更深。下面是设计用来验证CUDA环境的最小示例,主要功能是打印设备信息。
#include <cstdio> #include <cuda_runtime.h> int main() { int devCount = 0; cudaGetDeviceCount(&devCount); if (devCount == 0) { printf("No CUDA device found.\n"); return -1; } cudaDeviceProp prop; cudaGetDeviceProperties(&prop, 0); printf("Device name: %s\n", prop.name); printf("Compute capability: %d.%d\n", prop.major, prop.minor); printf("Total global memory: %.2f GB\n", (double)prop.totalGlobalMem / 1024 / 1024 / 1024); cudaDeviceReset(); return 0; }保存为test.cu,然后:
nvcc -o test test.cu ./test如果能看到设备名称、算力版本、显存大小,说明你的CUDA环境已经能正常编译和运行GPU程序了。
3.5 顺手把cuDNN装上
深度学习框架几乎都依赖cuDNN。虽然严格来说它不是CUDA Toolkit的一部分,但既然做深度学习,迟早要用到。先把cuDNN库文件解压,然后复制到CUDA目录下:
tar -xzvf cudnn-linux-x86_64-8.9.x_cuda12-archive.tar.xz cd cudnn-linux-x86_64-8.9.x_cuda12-archive # 复制头文件和库文件 sudo cp include/cudnn*.h /usr/local/cuda/include/ sudo cp lib/libcudnn* /usr/local/cuda/lib64/ # 给库文件加读权限 sudo chmod a+r /usr/local/cuda/include/cudnn*.h /usr/local/cuda/lib64/libcudnn*注意cuDNN版本要和你的CUDA版本匹配。比如CUDA 12.x的安装包里会有适配12.x的cuDNN包,千万别下错版本。还有一点,NVIDIA官网的cuDNN下载需要注册账号,这个步骤没法通过wget直接搞定,下载后把文件放到本地再复制即可。
验证一下cuDNN是否被正确识别:
cat /usr/local/cuda/include/cudnn_version.h | grep CUDNN_MAJOR -A 2如果有响亮的版本号输出,说明库已经装好。有些新版头文件名是cudnn_ops_infer.h之类的细节差异,解法都一样,搜索包含CUDNN_MAJOR定义的头文件即可。
4. 高频问题排查实录:黑屏、循环登录、掉驱动、版本冲突
4.1 驱动装完进不去桌面怎么办
这是最常见也是最让人崩溃的问题。装完驱动重启,系统卡在登录界面,输入密码后又跳回登录界面,或者干脆黑屏。
我从几十次折腾经验里总结出几个排查方向:
第一,检查nouveau有没有拉黑成功。登录到Ctrl+Alt+F2的虚拟控制台,执行lsmod | grep nouveau。如果还能看到nouveau,说明拉黑没生效。可能是配置文件名写错,或者update-initramfs之后没重启。
第二,看安全启动(Secure Boot)的情况。很多品牌机默认开启安全启动,NVIDIA驱动模块如果没有签名,内核会拒绝加载它。处理方式有两种:进BIOS关闭Secure Boot,或在内核模块加载时配置签名。对个人开发机来说,直接关闭Secure Boot最省事。
第三,注意是使用了闭源驱动还是开源内核模块。如果你的显卡是较新的Ampere或Ada Lovelace架构(比如RTX 30/40系列),推荐尝试NVIDIA的“open”内核模块;如果用的是比较老的显卡,还是用传统闭源模块更稳。.run安装程序在检测到新架构显卡时有时会直接使用open模块,有时又让你选择,选错就会出现能装上但启动时无法加载的场景。
第四,查看日志。在虚拟控制台执行:
journalctl -xe搜索nvidia、smi、gdm等关键词,能很直观地看到驱动加载失败的具体原因。比如缺失模块、符号错误、权限问题等,日志里都会明确提示。
4.2 升级内核后nvidia-smi消失
这种情况我遇到过太多次了。系统一升级内核,nvidia-smi显示“command not found”,GPU也找不到了。原因很简单:驱动的内核模块是针对旧内核编译的,新内核里没有这个模块。
前面我提到的dkms就是用来解决这个问题的。如果你在安装.run驱动时加了--dkms参数,那么驱动模块会注册到dkms里,内核升级时系统会尝试自动重新编译驱动。如果没有自动重编,可以手动执行:
sudo dkms install -m nvidia -v 550.54.15如果提示找不到模块,说明驱动注册信息丢了,可以先sudo dkms remove -m nvidia -v 550.54.15 --all,再重新运行驱动安装程序并确认带上--dkms参数。
如果是通过apt装的驱动,一般是nvidia-dkms-550之类的包,检查一下包是否还在,必要时sudo apt install --reinstall nvidia-dkms-550。
这个问题的核心就一句话:驱动模块要和当前内核版本对应。dkms是减轻这种痛苦的利器,建议所有用.run方式装驱动的人都用起来。
4.3 CUDA与PyTorch/TensorFlow版本不匹配
很多人好不容易装好CUDA,结果装PyTorch时发现:PyTorch的预编译包只支持CUDA 12.1,你却装了11.8,或者反过来。其实这种情况并不是装错了,而是你只需要安装对应当前CUDA版本的PyTorch版本即可。比如:
pip install torch --index-url https://download.pytorch.org/whl/cu121它会根据cu121自动选择匹配CUDA 12.1的版本。但要注意,如果GPU驱动版本过低,跑PyTorch时会报CUDA driver version is insufficient for CUDA runtime version。这种情况就只好升级驱动了。
如果已经安装了很多个CUDA版本,担心环境变量指向错误,可以用nvcc -V先确认当前生效的版本,再用which nvcc看看这个命令到底来自哪个路径下的软链接。这样能快速定位是不是环境变量配置的问题。
4.4 问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 安装时提示nouveau已加载 | 没拉黑nouveau或拉黑后没重启 | 配置blacklist文件后执行update-initramfs -u并重启 |
| 循环登录 | 驱动与显卡不匹配、Secure Boot开启 | 检查journalctl -xe,关闭Secure Boot,换open/closed模块 |
nvidia-smi:No devices were found | 驱动模块未加载 | dkms status查看模块信息,重新编译模块 |
nvcc: command not found | 环境变量没配好 | 检查~/.bashrc中的PATH配置并source |
运行时找不到libcudart.so | LD_LIBRARY_PATH缺失 | 确认/usr/local/cuda/lib64在环境变量里 |
| CUDA安装时勾选了Driver,导致驱动冲突 | 驱动版本被覆盖 | 重装正确版本的驱动,或用nvidia-smi核对版本 |
编译CUDA程序报错nvcc fatal | gcc和CUDA版本不兼容 | 安装对应版本的gcc,或用-ccbin参数指定编译器版本 |
5. 最后分享几条经验
很多官方文档只会告诉你“执行这个命令”,但我在实际使用中觉得,真正提高效率和降低试错成本的,反而是那些不起眼的习惯。这里挑几个我觉得特别管用的分享出来。
第一条经验,是“先备份,再动手”。我在装驱动之前一定会做一次系统快照,哪怕是虚拟机也留着快照。原因很简单:驱动和CUDA牵涉到底层系统库和内核模块,出了问题之后恢复的成本往往远高于备份的成本。如果你当时偷懒没备份,一旦反复黑屏,你会后悔的。
第二条经验,是“统一用.run方式管理驱动和CUDA”。如果用apt装驱动、run装CUDA,两者混在一起的版本状态其实很难理清。要么全用apt,要么全用run。我自己在开发机上一直用run方式,因为它把驱动、CUDA和cuDNN的安装过程全都变成“可重复执行的脚本”,以后重装系统,照着同一套流程来,结果是一致的。
第三条经验,是安装完成后的“固化配置”。每次装完驱动和CUDA后,我会把当前版本号、下载链接、安装参数、遇到的坑,以及最后的验证命令,都记在一个文本文件里,放到家目录下。别小看这个习惯,半年后当你需要在新机器上重装环境时,这份记录就是最大的财富。你在终端里敲过的每一条命令,都会在这个文件里有明确的下一步说明和优先级标注。
最后再说一下,如果你在安装过程中碰到了某个问题,并且查了很多资料都找不到答案,不妨把关键日志贴给社区,或者在技术论坛发帖讨论。我遇到过不少问题,最后都是靠别人的一句提示才恍然大悟。这本来就是一个需要“折腾”的过程,正常。希望你装的这次从开机到验证都顺顺利利。