直接上手说一个我在Ubuntu下折腾得最多的场景:装了N卡、跑着训练或渲染任务,结果风扇不是满转就是闲转,显卡温度80度了风扇还在那“思考人生”。很多朋友第一反应是装个温控软件,结果发现Linux底下根本没有Windows那种傻瓜式面板,核心原因就是没搞明白GPU、主板、PWM风扇这三者之间到底是谁在控制谁。
这篇就围绕Ubuntu系统下主板GPU温控PWM风扇这条链路,把原理、方案、实操配置和排查方法一次讲透。适合刚装完Ubuntu的深度学习用户、跑渲染或者挖矿类重负载任务的玩家,以及被“风扇策略不听话”折磨过的人。读完你能搞清楚两件事:你的风扇到底接在哪个控制器上,以及怎么让风扇转速真正跟随GPU温度曲线变化。
1. 先别急着配软件,把GPU到风扇的信号链路捋清楚
1.1 PWM风扇调速的基本原理
PWM是脉冲宽度调制,也就是用一个固定频率(风扇上通常是25kHz左右)的方波信号,通过改变高电平占空比来控制功率输出。对于4针PWM风扇,第4脚专门用来接收这个控制信号,占空比越高,平均电压等效值越高,风扇就转得越快。这个机制决定了我们不需要风扇一直全速,而是可以根据温度实时调整占空比。
3针风扇没有PWM信号线,它是靠主板改变供电电压来调速的,这种方式响应慢且不能做到精细的百分比控制。所以如果你的风扇是3针接口,想做到精确的温度曲线控制就有点吃力,通常只能用电压档位控制或者干脆恒定转速。
顺带说一个细节:很多主板上的PWM控制信号是由EC(嵌入式控制器)或者Super I/O芯片产生的,这些芯片内部固化了温控策略。温度传感器读到哪里、对应多大的占空比,基本都在BIOS/EC固件里写死。所以有一些风扇接在主板上之后,无论你怎么调系统参数它都不动,因为控制权根本不在操作系统那边。
1.2 Ubuntu下风扇“失控”到底失在哪
在Windows里,厂商软件如MSI Afterburner、ASUS GPU Tweak能直接接管NVIDIA显卡风扇,是因为这些软件调用了显卡BIOS里开放的PWM控制寄存器。Ubuntu下默认的NVIDIA闭源驱动虽然也能管理风扇,但它的默认行为是“让显卡自己按内部曲线来”,很多非公版卡的第一步就是直接忽略用户设置,只有公版卡或者支持NVML风扇控制的显卡才听话。
另一条路是主板风扇接口。很多台机主板上的SYS_FAN、CHA_FAN接口本身支持PWM控制,但它们的温度源默认接的是CPU温度或者主板某处温度传感器,而不是GPU温度。这就导致了:GPU已经热到烫手,主板那侧的风扇依然按CPU温度低速运行,整套风道处于“盲调”状态。
搞清楚这两条链路后,方案选择就清晰了。接下来我把常见可行方案逐一展开。
2. 方案选型:软控、驱动参数、硬件兜底,三条路看情况选
2.1 先分清风扇插在哪个接口
动手之前,打开机箱看一眼风扇的线到底接在显卡PCB上,还是接到了主板。这个决定了后续所有的操作方向。
显卡自带风扇通常是从显卡尾部引线,插在PCB上的一个小型4针接口上;而机箱风扇是接主板上的SYS_FAN或者CHA_FAN。两种情况对应完全不同的控制方式:
- 显卡原生接口:需要靠显卡驱动和显卡BIOS支持,控制对象是显卡上的PWM控制器,软件层面用NVML或者nvidia-settings读写。
- 主板接口:控制对象在主板EC固件里,要么用主板的BIOS温度策略,要么在Ubuntu里用fancontrol写hwmon文件。
我用一个表格快速对比一下主流控制方案:
| 方案 | 控制对象 | 温度源 | 适用场景 | 主要限制 |
|---|---|---|---|---|
| NVIDIA驱动参数 | 显卡板载风扇 | GPU核心/显存 | N卡玩家、公版或支持NVML调扇的显卡 | 部分非公版卡不开放权限 |
| fancontrol | 主板SYS_FAN等PWM接口 | 主板温度传感器或PCIe附近传感器 | 想把机箱风扇随GPU温度联动 | 温度源未必能找到GPU点 |
| BIOS/EC策略 | 主板PWM口 | 主板指定传感器 | 最简单的开机即生效 | 一般只支持CPU温度源 |
| 独立PWM温控器(如555电路或MCU) | 外接风扇 | 自带NTC热敏电阻或外部信号 | 需要彻底跳过主板/驱动 | 需要动手能力,无系统数据回传 |
2.2 方案A:NVIDIA驱动参数直接控风扇
很多N卡是可以通过加载参数来控制风扇的,常见做法是在内核模块配置里写入两个键值:NVreg_RegistryDwords。这个参数可以把NVIDIA驱动内部注册表的对应项直接改掉,其中和风扇相关的两个键是:
GPUFanControlState=1GPUTargetFanSpeed=70
前者把风扇控制权限从显卡固件手里接管过来,后者设定目标转速百分比。这种方式适合固定转速场景,比如长期满载挖矿或渲染,但如果想要动态温度曲线,就需要配合脚本周期性改写GPUTargetFanSpeed。
要注意的是,这个参数不是对所有N卡都有效。很多显卡固件并没有开放PWM控制寄存器,即使你设了GPUFanControlState=1,风扇也纹丝不动。判断方法很简单:设置后执行nvidia-smi -q -d FAN,看风扇转速是否变化,如果一直是N/A就是没接管成功。
2.3 方案B:lm-sensors配合fancontrol实现主板PWM温控
这个方案是用Ubuntu里开源工具链lm-sensors读取主板传感器、生成/etc/fancontrol配置文件,然后由fancontrol守护进程周期性输出PWM信号到主板风扇接口。
fancontrol的好处是它知道主板上有多少个PWM输出口,并且允许你把指定温度传感器映射到指定PWM输出。GPU温度不太容易直接读进来,但可以想办法借助NVML脚本把GPU温度写入一个虚拟文件,再让fancontrol读这个文件,曲线就能围着GPU温度走。具体操作下一章详述。
2.4 方案C:独立硬件温控器“兜底”
如果你主板老、BIOS策略僵化,或者装的是那种服务器主板,根本不提供PWM风扇接口给普通风扇,那软件层基本没戏,只能考虑外接硬件温控器。常见有两种:
一种是用NE555搭建PWM温控电路,利用热敏电阻(NTC)分压改变555的占空比,从而实现“温度越高风扇越快”。这方案纯模拟,不需要系统参与,缺点是温控区间和曲线完全靠电阻参数硬定,后期调整比较费劲。
另一种是用单片机,比如STM32或者Arduino读温度传感器,输出PWM去驱动风扇。这种方式可以做得非常精细,还能把转速通过串口上报,但如果只是给一台电脑降温,投入产出比偏低。除非是玩硬件DIY,否则我建议先试软件方案,实在不行再考虑外置温控模块。
3. 完整实操:Ubuntu下配置GPU联动PWM温控风扇
下面这套流程是我在Ubuntu 20.04/22.04上多次实践过的,目标是实现“GPU温度升高,机箱风扇或显卡风扇PWM跟随变化”,并且重启后依然生效。
3.1 第一步:确认显卡、驱动和传感器状态
先跑几条命令收集基础信息:
# 查看显卡型号 lspci | grep -i vga # 查看NVIDIA驱动和GPU温度 nvidia-smi nvidia-smi -q -d TEMPERATURE如果nvidia-smi显示驱动正常,但是风扇转速一栏是N/A,说明当前驱动看不到显卡风扇转速,这往往也意味着直接改驱动参数没法控制风扇。
接着安装基础监控工具:
sudo apt update sudo apt install lm-sensors fancontrol sudo sensors-detect --autosensors-detect扫描过程会问一堆问题,用--auto自动选择安全选项。扫描完执行sensors,你应该能看到类似coretemp、nct6775、it8686之类的主板监控芯片信息,这些就是hwmon设备和温度传感器。
如果sensors里看不到任何主板传感器,大概率是监控芯片比较新或比较偏,需要手动加载对应内核模块。可以用sudo sensors-detect之后的提示信息,按它建议的模块名手动加入/etc/modules,然后sudo modprobe对应模块。
3.2 第二步:找到PWM输出文件
风扇控制的核心接口在/sys/class/hwmon/下面,每个hwmon设备对应一个目录,里面有temp*_input、pwm*、fan*_input等文件。执行下面命令把全部设备树打出来:
for hw in /sys/class/hwmon/hwmon*; do echo "== $hw ==" ls $hw for pwm in $hw/pwm*_enable; do echo "--- $pwm ---" cat $pwm done done重点关注存在pwm1、pwm2这类文件的目录,这就是能调节的主板PWM输出。用cat /sys/class/hwmon/hwmon*/fan1_input可以读取对应风扇转速,如果读数一直是0,说明这个通道没有接风扇,或者风扇是3针不支持测速。
这一步骤的关键是锁定“哪个PWM通道控制哪个风扇”。简单做法是给某个PWM写不同占空比,然后观察风扇声和转速变化:
# 先看当前权限,可能需要root sudo su # 手动控制PWM1输出,占空比30%(数值0-255) echo 1 > /sys/class/hwmon/hwmonX/pwm1_enable echo 76 > /sys/class/hwmon/hwmonX/pwm1 sleep 5 # 再切到70%,听声音或看转速 echo 178 > /sys/class/hwmon/hwmonX/pwm1 sleep 5提醒:pwm_enable的值含义是控制模式,0表示不控制,1表示手动PWM模式,2表示自动温控模式。把pwm_enable写成1后才能手动写pwm值。
3.3 第三步:pwmconfig生成fancontrol配置
fancontrol包自带一个交互式脚本pwmconfig,它会扫描当前可以控制的PWM输出,并引导你设置温度上下限、风扇启停阈值、启动转速等,最后生成/etc/fancontrol。
sudo pwmconfig脚本会列出检测到风扇的PWM通道,教你确认每个PWM映射到哪个风扇口,然后让你设定温度区间。关键参数概念我先解释清楚:
MINTEMP:低于这个温度,风扇以最小转速运行。MAXTEMP:达到这个温度,风扇输出最大转速。MINSTART:风扇启动时需要的占空比,也就是克服静摩擦的“启动电压”对应值。MINSTOP:风扇停止时的占空比,低于这个值可能直接停转。
整个配置生成的/etc/fancontrol格式大概是:
INTERVAL=10 DEVPATH=hwmon0=devices/platform/coretemp.0 DEVNAME=hwmon0=coretemp FCTEMPS=hwmon0/pwm1=hwmon0/temp1_input FCFANS=hwmon0/pwm1=hwmon0/fan1_input MINTEMP=hwmon0/pwm1=40 MAXTEMP=hwmon0/pwm1=75 MINSTART=hwmon0/pwm1=70 MINSTOP=hwmon0/pwm1=50这行配置的含义是:把coretemp的温度作为温度源,控制pwm1,温度在40-75度之间线性调整,最低启动占空比70/255,停止占空比50/255。
在GPU联动之前,先用这套配置确认主板PWM通道物理上工作正常。配置完启动服务:
sudo systemctl enable fancontrol --now如果报错“Configuration file /etc/fancontrol does not exist”,重新跑pwmconfig并确保中间步骤没被跳过。
3.4 第四步:把GPU温度注入Fancontrol的控制器
fancontrol的温度源必须是某处hwmon的temp*_input文件,而GPU温度默认不在hwmon里。解决办法是做一个“虚拟温度桥”,用一个shell脚本或服务周期性地把GPU温度写入一个hwmon节点。
更省事的办法是找一个临近GPU位置的传感器作为温度源。很多中高端主板在PCIe插槽附近有T_Sensor或主板上板载的热敏电阻接口,如果BIOS支持把它映射到hwmon,那fancontrol直接读它就行。但很多主板的T_Sensor接口要引出外部热敏探头,不如软件桥来得简洁。
下面是一个把NVIDIA GPU温度同步到hwmon虚拟文件的Cron脚本思路:
#!/bin/bash # 定时把GPU温度写入 /tmp/gpu_temp # 之后通过udev或systemd路径让fancontrol读取 gpu_temp=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits | head -n1) echo $gpu_temp > /tmp/gpu_temp因为fancontrol默认只读hwmon目录下的标准文件,直接在/tmp下写文件是给它读不到的,需要绑定一个hwmon目录。更实用的做法是直接在fancontrol配置里加一行sysfs映射回填,或者用lm-sensors的sensord思路去写。也可以选择让fancontrol的DEVPATH直接指向一个temp1_input软链接。
我给你一个最稳的代理方案:写一个扇区监控守护进程去做两件事——周期性读GPU温度、写/sys/class/hwmon/hwmonX/temp1_input。hwmon下很多节点本身就是可写的,只要你有root权限。但这样做会和主板监控芯片冲突,因为那个文件原本是芯片自动更新的,强行写入可能被芯片覆盖掉。适合做代理的是一个“虚拟hwmon”驱动,或者干脆用一个独立温度通道接一个NTC探头再用算法换算,但这对大多数用户来说太复杂了。
实操中我更推荐另一个折中方案:用NVML脚本控制的是显卡自身的PWM,而不是主板PWM。前面用fancontrol配置主板PWM,再把显卡风扇接到主板接口上,就会引入另一个问题:显卡BIOS会检测不到风扇转速,部分显卡会因为Fan Fail直接降频或者开机报错,所以这个改造只适用于机箱风扇,不适合显卡原生风扇。
3.5 第五步:NVIDIA显卡原生风扇脚本控制
对于NVIDIA公版卡或者部分支持NVML的卡,直接写一个控制脚本就行。最基础的手动控制:
# 开启风扇控制状态 nvidia-settings -a "[gpu:0]/GPUFanControlState=1" # 设定50%转速 nvidia-settings -a "[fan-0]/GPUTargetFanSpeed=50"把温控脚本做成循环:
#!/bin/bash # gpu_fan_curve.sh # 根据GPU温度动态调整风扇转速 while true; do temp=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits | head -n1) if [ $temp -lt 50 ]; then speed=40 elif [ $temp -lt 65 ]; then speed=60 elif [ $temp -lt 75 ]; then speed=80 else speed=100 fi nvidia-settings -a "[gpu:0]/GPUFanControlState=1" >/dev/null nvidia-settings -a "[fan-0]/GPUTargetFanSpeed=$speed" >/dev/null sleep 5 done这个脚本思路很直白:每5秒读一次GPU温度,根据分段区间设置风扇转速。分段温度区间和对应转速就是温控策略,也是整个PWM温控的“灵魂”所在。
曲线设置的思路,我会在下一节详细展开。之后把这个脚本做成systemd服务即可开机自启。
3.6 第六步:开机自启配置
无论用哪种方案,都要把它交给systemd管理,保证重启后自动生效。
写一个服务文件/etc/systemd/system/gpu-fan-control.service:
[Unit] Description=GPU Fan Curve Control After=multi-user.target [Service] Type=simple ExecStart=/usr/local/bin/gpu_fan_curve.sh Restart=always RestartSec=5 [Install] WantedBy=multi-user.target启用服务:
sudo systemctl daemon-reload sudo systemctl enable gpu-fan-control --now这里有个细节:很多桌面环境会在开机时调用nvidia-settings恢复当前风扇状态,如果你同时启用了自研脚本,可能出现两个进程互相抢风扇控制权。解决方法是只保留一个控制进程,并且不要勾选“自动应用开机设置”这类选项。
4. 温控曲线的设计思路与参数计算
很多人搞温控只关心“能不能调速”,结果曲线设置得跟心电图一样,忽高忽低,风扇寿命反而缩短。这里讲两个关键概念:温度迟滞和转速平滑。
4.1 为什么要设计“迟滞区间”
假设你用单一阈值:温度超过60度就拉满,低于60度就最小转速。那实际温度在60度附近抖动的几秒钟内,风扇就会反复从最低跳到最高再跳回来,转速声音都是尖锐且突兀的,这就是没有迟滞的后果。
迟滞的本质是“升温和降温走两条曲线”。比如升温时达到70度才拉高,但降温时低于65度才降低转速,这样中间有5度的缓冲区,风扇不会频繁切换状态。
简化的实现方式是在脚本里引入“当前状态记忆”:
if [ "$state" = "low" ] && [ "$temp" -gt 70 ]; then state="high" elif [ "$state" = "high" ] && [ "$temp" -lt 60 ]; then state="low" fi4.2 转速百分比与PWM占空比的换算
风扇转速和PWM占空比近似线性关系,但不是严格线性。比如同一个风扇,占空比50%不一定转速就是最大转速的50%。因为电机需要克服启动摩擦,通常在低占空比下直接不转。所以MINSTART要设置在“刚好能启动”的数值之上,否则会出现“设置30%但它不转”的假故障。
我实际测算过一个12cm机箱风扇的典型参数:
| 占空比 | 实测转速(RPM) | 状态 |
|---|---|---|
| 0% | 0 | 停转 |
| 15% | 0 | 堵转,电机低频啸叫 |
| 30% | 620 | 稳定转动 |
| 50% | 1050 | 安静 |
| 70% | 1550 | 明显风噪 |
| 100% | 2100 | 满载 |
这个表说明MINSTART至少要30%左右,而最小运行转速建议设定在35%-40%,不要贴着启动阈值跑,避免微小温差变化导致启停反复。
4.3 一条合理曲线的设计样例
以一张功耗250W的RTX显卡作为参照,待机45度,满载80度,风扇目标转速设定如下:
- 40-50度:30%-40%占空比,风扇低速运行,保证基本风道。
- 50-65度:40%-70%,线性提升,这个区间是中度负载变化区,关注风噪和温度的平衡。
- 65-80度:70%-100%,显卡已经进入高负载区,优先保散热不要管噪音。
- 80度以上:维持100%,同时建议检查机箱风道。
脚本实现时不要每次根据温度直接覆盖转速,而是做一个递进调整,每次改变不超过10个百分点:
if [ $new_speed -gt $current_speed ]; then current_speed=$((current_speed+5)) # 升速步进5% elif [ $new_speed -lt $current_speed ]; then current_speed=$((current_speed-5)) # 降速步进5% fi这样风扇转速变化不会产生机械冲击,也避免了转速在阈值点反复横跳。
5. 常见问题与排查技巧实录
这一节把我在多个机器上踩过的坑和常见问题整理成表格,直接照表排查能省很多时间。
| 现象 | 可能原因 | 排查方法 | 解决办法 |
|---|---|---|---|
| nvidia-smi看不到风扇转速 | 非公版卡未开放风扇传感器 | nvidia-smi -q -d FAN返回N/A | 改用显卡自带控制参数尝试,不行就外接硬件温控 |
| fancontrol报错找不到设备 | PWM设备不在默认hwmon索引 | journalctl -u fancontrol -e看报错 | 设备路径用/sys/class/hwmon/hwmonX实际路径并调整DEVPATH |
| 风扇设置后不转 | MINSTART低于电机启动阈值 | 手动echo不同占空比测试 | 提高到启动转速以上并留余量 |
| 温度一条直线不变 | 读错了传感器或者被其他进程占用 | sensors观察哪个温度在变 | 切换温度源到coretemp或nvidia-smi独立读取 |
| 风扇满速无法降下来 | 系统有其他进程在占着PWM控制权 | `ps aux | grep -i fan` |
| 机箱风扇随GPU温度变化无效 | 风扇接的主板接口温度源不是GPU | 查看BIOS里该风扇接口绑定的温度源 | 设置T_Sensor或改脚本写hwmon虚拟温度 |
| 重启后配置丢失 | systemd服务没启用或顺序不对 | systemctl status fancontrol | systemctl enable fancontrol并确保在图形界面启动前 |
5.1 关于主板T_Sensor接口的一点经验
部分高端主板有一个专门外接温度探头的T_Sensor引脚,这个引脚在BIOS里可以被指定为某个风扇接口的温度源。如果你不想搞写hwmon的骚操作,可以把一个NTC热敏电阻贴在显卡背板上,接到T_Sensor,再去BIOS设置里选“使用T_Sensor作为风扇控制温度源”,这样主板会自动读外部探头温度,硬件层就能闭环了,不需要任何软件介入。
但要注意:这个方案的风扇控制策略依然在BIOS里设定,一般只有几个固定曲线可选,做不到像脚本那样精细调节。好处是稳定可靠,适合那些不想折腾软件的用户。
5.2 排查实践中特别容易踩的两个坑
第一个是“风扇测速为0导致主板进保护”。有些主板检测到某个风扇接口转速为0时,会默认全速运转并报警。如果你把风扇从3pin或者显卡接口接到主板PWM口,测速线没有接好,主板就会一直满速输出。解决方法是确认测速线(第3针)确实连到了主板的测速引脚,同时确认风扇支持RPM信号输出。
第二个是“nvclock和nvidia-settings旧参数失效”。老的教程喜欢用nvclock -f来控制风扇,但现在的新版驱动已经把这个接口移除了。推荐使用nvidia-settings的GPUFanControlState和GPUTargetFanSpeed组合,或者直接用NVML的Python API来写控制逻辑,后者在驱动兼容性上更稳。
6. 一点硬件层面的补充思路
如果软件方案试了一圈还是无法控制,比如服务器主板完全不提供PWM输出到普通风扇,那就只能硬件上另起炉灶。
我在一台旧工作站上用过类似方案:用一个独立的温控模块去读贴在GPU背板上的NTC热敏电阻,模块输出PWM直接驱动一个12cm风扇,电源取自独立12V供电。整个过程和主板、驱动完全无关,GPU温度一旦升高,散热模块按自己的硬件曲线加速,非常省心。
这类温控模块设计核心是三块:温度采集用的NTC分压电路、PWM产生用的555定时器或单片机、驱动风扇用的MOS管。用555搭建时,振荡频率设到25kHz左右,避免产生高频啸叫。具体电阻参数通常用串联NTC和电位器调节,电位器用来调基准电压从而改变温度-占空比曲线的斜率,实际调试时需要一边加热探头一边测转速,比较费时间。
如果你要自己画电路或者自己写单片机固件,建议直接用Arduino的PWM输出驱动一个三极管,配合10k NTC加上拉电阻,代码逻辑基本就是ADC采样后映射成PWM输出。这方便也够灵活,唯一的门槛是得有一颗想折腾的心。
7. 最后想说点实在话
我个人的经验是,在Ubuntu下能把PWM温控玩明白的人,通常也把散热风道和功耗控制都理顺了。因为这个东西需要你理解散热系统的数据流、控制流和物理约束,不像Windows里装个软件一键“智能温控”那么无脑。
最推荐的路线其实是能软件解决的问题不要拖到硬件,能主板解决的不要拖到系统。如果你用的是普通消费级主板加消费级显卡,优先在BIOS里把风扇接在可调温源的PWM口上,然后用系统脚本辅助微调;如果主板已经老到没有灵活的温控策略,再考虑外置模块兜底。折腾之前记得先备份BIOS设置,做一个风扇转速的初始记录,这样排查时会轻松很多。