Linux 风扇控制与 PVE 静音实战:从 hwmon 到 EC 直控
Linux 风扇控制与 PVE 静音实战:从 hwmon 到 EC 直控
Linux 风扇控制没有一套适用于所有设备的通用命令。台式机主板通常提供标准 PWM,笔记本依赖厂商接口,服务器可通过 BMC/IPMI,而部分迷你主机只有嵌入式控制器(EC)知道如何控制风扇。
本文先建立选择路径,再结合一台 PVE 迷你主机说明:如何把容易抖动的单温度脚本,改造成具有多传感器、迟滞、读回验证和自动回退的安全控制器。
先识别控制链路
Linux 硬件监控通常通过 hwmon 暴露:
for h in /sys/class/hwmon/hwmon*; do
printf '%s: ' "$h"
cat "$h/name"
find "$h" -maxdepth 1 \( -name 'temp*_input' -o -name 'pwm*' -o -name 'fan*_input' \) -print
done常见文件:
| 文件 | 含义 |
|---|---|
temp1_input | 毫摄氏度,45000 表示 45°C |
fan1_input | 实际 RPM |
pwm1 | PWM 值,常见范围 0–255 |
pwm1_enable | 1 通常为手动,2 通常为自动 |
根据探测结果选择工具:
- 有可写
pwm*:优先lm-sensors+fancontrol。 - ThinkPad 等笔记本:使用
thinkfan、nbfc-linux等机型工具。 - 带 BMC 的服务器:优先厂商支持的自动策略,必要时使用 IPMI。
- 只有温度、没有 PWM:最后才考虑逆向 EC。
标准主板:fancontrol
Debian/Ubuntu 的基本流程:
sudo apt install lm-sensors fancontrol
sudo sensors-detect
sensors
sudo pwmconfig
sudo systemctl enable --now fancontrolpwmconfig 会短暂改变风扇速度来确认 PWM 与风扇的对应关系。不要在无人值守的高负载主机上盲目运行,生成 /etc/fancontrol 后也要人工检查。
INTERVAL=10
FCTEMPS=hwmon0/pwm1=hwmon0/temp1_input
FCFANS=hwmon0/pwm1=hwmon0/fan1_input
MINTEMP=hwmon0/pwm1=40
MAXTEMP=hwmon0/pwm1=75
MINSTART=hwmon0/pwm1=50
MINSTOP=hwmon0/pwm1=30
MINPWM=hwmon0/pwm1=30
MAXPWM=hwmon0/pwm1=255PWM 占空比不等于转速百分比。风扇有启动阈值和最低稳定转速,所以 MINSTART 往往高于 MINSTOP。低温完全停转也不一定适合有 NVMe、内存和网卡持续发热的服务器。
为什么单温度脚本会很吵
最初的 PVE 风扇脚本每隔数秒读取 Ryzen Tctl,直接映射风扇档位。日志中会出现类似:
12% → 80% → 60% → 20% → 12% → 35% → 60% → 12%原因并不是散热能力不足,而是算法设计有问题:
- 睿频带来持续几秒的 Tctl 尖峰;
- 降档没有独立退出阈值;
- 稳定计数没有要求“连续满足”;
hwmon4这类编号被写死,重启后可能指向另一个设备;- 读取失败时错误地假定低温;
- 服务退出后仍把 EC 留在手动低转速;
- 只看 CPU,忽略共享内存、核显、NVMe 和网卡。
多传感器控制架构
更安全的做法不是直接取最高温度,而是让每种硬件拥有适合自己的曲线,最后取最高需求档位:
最终档位 = max(CPU 曲线, GPU 托底, 内存托底, NVMe 托底, 网卡托底)CPU 是快速主信号;其他设备用于持续热量保护。示例 CPU 曲线:
| 平滑温度 | 风扇档位 |
|---|---|
| <68°C | 12% |
| 68–74°C | 18% |
| 74–79°C | 25% |
| 79–84°C | 35% |
| 84–88°C | 50% |
| 88–92°C | 70% |
| ≥92°C | 100% |
使用指数移动平均过滤短峰值:
filtered = (filtered × 7 + raw) / 8普通升档要求连续命中数次;降档要求更长稳定时间,并且每次只降一级。与此同时保留原始温度紧急通道,例如达到 92°C 立即满速,不能等待平滑值追上。
EC 直控只能作为最后手段
本机没有标准 PWM 节点,只能从 ACPI DSDT 中确认风扇模式和占空比寄存器。不同型号的寄存器完全可能不同,不能照抄地址。
sudo apt install acpica-tools
sudo sh -c 'cat /sys/firmware/acpi/tables/DSDT > /tmp/dsdt.dat'
iasl -d /tmp/dsdt.dat
rg -n 'FAN|FCMI|WMAA|WMAB' /tmp/dsdt.dsl只有在 ACPI 方法中确认写入语义后,才能使用经过审查的 EC 工具。生产脚本至少要做到:
- 只写已确认的寄存器;
- 每次写入后立即读回;
- CPU 传感器连续失败就恢复固件自动模式;
EXIT、INT、TERM都执行回退;- systemd 再用
ExecStopPost做第二层回退; - 高温直接进入最高档。
示意单元:
[Unit]
Description=Safe EC fan controller
After=multi-user.target
[Service]
ExecStart=/usr/local/sbin/fan-daemon
ExecStopPost=/usr/local/sbin/fan-control auto
Restart=on-failure
RestartSec=3
[Install]
WantedBy=multi-user.target在启用开机启动前,应先手动停止服务,确认 EC 确实回到 BIOS/固件自动控制。
用真实负载验收
只运行 CPU 压力工具无法覆盖核显和共享内存。本次使用实际的 Vulkan 语音识别工作负载进行多路并发,持续记录 CPU、Radeon 核显、两条内存、NVMe、网卡、档位和档位所有者。
最终测试中,CPU 峰值约 66.5°C,核显约 65°C,内存最高约 56°C。风扇只从 12% 升到 18% 和 25%,没有进入高噪声档位,也没有来回抖动;负载结束后按迟滞规则逐级回落。
这个结果还揭示了一个容易忽略的问题:持续核显计算时,内存温度可能比 CPU 更早决定机箱风量。只看 CPU 会漏掉真正的持续热源。
日志与调参边界
建议每 30 秒写一条结构化遥测:
telemetry cpu=55.4/54.8C gpu=50.0/49.2C ram=52.9/52.2C \
nvme=38.3C nic=57.1C duty=25% owner=ram查看调档和异常:
journalctl -u fan-daemon.service --since today -o cat | grep change
journalctl -u fan-daemon.service --since today -o cat | grep -E 'WARNING|ERROR'每次修改曲线后都应重复相同压力测试。静音和温度是明确的取舍:如果要求某个设备永远不超过厂商预警线,就必须接受更早升档、更高噪声,或改善机箱进风,而不是继续压低风扇。
总结
- 先使用标准 hwmon 与厂商控制路径,EC 直控是最后选择。
- 风扇控制器必须有平滑、独立回差、多传感器和高温快速通道。
- 任何读取或写入异常都应回到固件自动模式,而不是保持最后的低档。
- 用真实业务负载验证,并保留结构化温度与调档日志。
静音不是把占空比写得尽可能低,而是在可验证、可回滚的保护边界内,减少没有散热意义的短促升降档。
