问题不是“Linux 续航差”这么简单
这次折腾的起点很朴素:同一台机器,Windows 下中度使用还能撑四五个小时,Arch Linux 下离电省电之后却只剩一小时二十分钟左右。
机器是幻 16 Air 2025,Intel Core Ultra 9 285H 加 RTX 5070 Ti Laptop GPU。我的目标并不是把独显彻底禁掉,因为它对 CUDA、AI 推理、渲染和一些计算任务仍然有价值。我真正想要的是:
- 桌面显示只走核显。
- 普通 GUI 程序默认不碰独显。
- 独显只作为计算卡存在。
- 空载时独显必须能 runtime suspend。
- 需要 CUDA 或显式查询时再自动唤醒,用完自动睡回去。
这几个目标看起来很自然,但在 Linux 混合显卡环境里,它们不是同一件事。
先分清三种“独显没用”
排障时最容易误判的地方,是把下面三种状态混在一起:
第一种是独显不负责显示。内屏接在核显上,nvidia-smi 里 Display Active 也不是 active。这只能说明它不是显示卡。
第二种是没有用户进程打开 /dev/nvidia0 或 /dev/dri/renderD*。这说明当前没有普通应用直接占用独显。
第三种才是最关键的:PCI 设备本身进入 runtime suspend。也就是:
cat /sys/bus/pci/devices/0000:01:00.0/power/runtime_status
输出应该是:
suspended
前两种状态都成立时,独显仍然可能保持 P8、空载烧几瓦电。我的机器一开始就是这种状态:桌面看起来没用独显,但 NVIDIA 仍然 active。
第一刀:别让桌面和 GLVND 枚举 NVIDIA
Hyprland 配置里写 env = DRI_PRIME,0 不够。这个变量主要影响 Hyprland 启动后的子进程,而 Hyprland 自己在启动阶段就可能通过 GLVND 枚举到 NVIDIA EGL vendor,然后加载 libEGL_nvidia.so 一类库。
最后有效的是给 Hyprland 的 systemd user unit 加 drop-in,让它在进程启动前就只看 Mesa EGL vendor:
[Service]
Environment=__EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json
Environment=DRI_PRIME=0
UnsetEnvironment=__NV_PRIME_RENDER_OFFLOAD __GLX_VENDOR_LIBRARY_NAME GBM_BACKEND LIBVA_DRIVER_NAME NVD_BACKEND
我的文件位置是:
~/.config/systemd/user/[email protected]/10-force-mesa-egl.conf
重登之后验证:
pid=$(pidof Hyprland | awk '{print $1}')
rg -i 'nvidia|libcuda' /proc/$pid/maps
lsof -nP /dev/nvidia0 /dev/nvidiactl
都应该没有输出。这里的重点不是“核显优先”,而是让 compositor 自己别在启动时顺手碰一下 NVIDIA。
第二刀:不要启动 NVIDIA 图形栈
我的目标是计算卡,不是 PRIME 图形 offload。所以 nvidia_drm 和 nvidia_modeset 对这个目标反而是负担。
我做了三件事。
先从 initramfs 的早期模块列表里移除 NVIDIA 图形栈。原来 /etc/mkinitcpio.conf 里有:
MODULES=( nvidia nvidia_modeset nvidia_uvm nvidia_drm )
改成不预加载这些模块,然后:
sudo mkinitcpio -P
再加一个 modprobe 配置,把 NVIDIA DRM/modeset 挡住,只保留核心驱动给 CUDA 使用:
# /etc/modprobe.d/zz-nvidia-compute-only.conf
blacklist nvidia_drm
blacklist nvidia_modeset
install nvidia_drm /usr/bin/false
install nvidia_modeset /usr/bin/false
options nvidia NVreg_DynamicPowerManagement=0x02
options nvidia_drm modeset=0 fbdev=0
最后把 nvidia-utils 默认开机加载 nvidia-uvm 的行为盖掉:
sudo ln -sfn /dev/null /etc/modules-load.d/nvidia-utils.conf
这样重启后,nvidia_drm 和 nvidia_modeset 不再出现:
lsmod | grep '^nvidia_drm\|^nvidia_modeset'
无输出才是我想要的状态。
第三刀:TLP 的 performance 档也不能把计算卡点亮
最隐蔽的问题在 TLP。
我当时已经做到 Hyprland 不碰 NVIDIA,nvidia_drm 和 nvidia_modeset 也不加载,甚至卸掉 nvidia_uvm 后,独显还是 active。最后发现真正卡住它的是:
/sys/bus/pci/devices/0000:01:00.0/power/control = on
原因是 TLP 的 AC/performance 配置里有:
RUNTIME_PM_ON_AC=on
这个配置对大部分性能档场景合理,但对“只作为计算卡的独显”不合理。计算卡空载也应该睡眠,不应该因为插电或 performance 档就常亮。
解决方法是给 NVIDIA 两个 PCI function 单独开 runtime PM:
# /etc/tlp.d/02-nvidia-compute-only.conf
RUNTIME_PM_ENABLE="01:00.0 01:00.1"
然后:
sudo tlp start
这一步验证非常直接:
cat /sys/bus/pci/devices/0000:01:00.0/power/control
cat /sys/bus/pci/devices/0000:01:00.0/power/runtime_status
目标输出:
auto
suspended
这里有个小坑:TLP 配置行不能缩进。前面多两个空格,tlp-stat -c 就不会识别它。
验证要避免自己把独显唤醒
很多 NVIDIA 检查命令本身会唤醒独显,尤其是:
nvidia-smi
所以日常判断独显睡没睡,优先看 sysfs:
cat /sys/bus/pci/devices/0000:01:00.0/power/runtime_status
我最后的稳定状态是:
supergfxctl = Hybrid
01:00.0 NVIDIA GPU = control auto, runtime suspended
01:00.1 NVIDIA audio = control auto, runtime suspended
Hyprland = 无 NVIDIA 库、无 /dev/nvidia* 句柄
TLP = 已读取 RUNTIME_PM_ENABLE="01:00.0 01:00.1"
我还做了一次受控唤醒测试:先确认空载是 suspended,再运行 nvidia-smi。独显会短暂醒来,随后在没有用户进程持有的情况下自动回到 suspended。
这就说明目标达成了:它不是被永久禁用,而是按需唤醒的计算卡。
代价和边界
这套方案不是“最通用”的混合显卡方案,而是有明确取舍的 compute-only 方案。
代价很清楚:
prime-run这类 NVIDIA 图形 offload 不再是默认目标。- 如果外接屏物理接在独显输出上,可能不可用。
- 某些依赖 NVIDIA EGL/GLX 的 GUI 工作流需要单独恢复图形栈。
nvidia-smi会唤醒独显,不能拿它当无干扰监控。
但换来的状态也很干净:
- 桌面渲染归核显。
- 普通应用不会把独显误唤醒。
- CUDA/计算仍能用。
- 独显空载真正 runtime suspend。
- TLP 的 performance/AC 档也不会把它强行保持在
on。
对我这类“核显足够强,独显主要用于计算”的使用方式来说,这比来回切 MUX、注销、重启更符合日常。
最后真正耗电的是别的东西
独显睡下去之后,整机功耗确实下降了。后来再看功耗,独显已经不再是问题,新的大头变成了 240Hz 屏幕、浏览器、Hyprland、Waybar、多个 Codex/OMX watcher、开发服务和微信这些常驻任务。
这也算是一个提醒:笔记本续航排障不要只盯着一个“嫌疑犯”。独显常亮很刺眼,也确实该处理;但它睡下去以后,系统里那些看似不起眼的后台和高刷新屏幕,才会成为下一层瓶颈。
调电源管理就是这样,一层一层剥。先把最离谱的错误状态纠正,再看真实工作负载。