背景与需求
我有一台自组 NAS,配置大致是:
- 主板**:ASRock B250M-HDV**
- 硬盘**:目前 4 块 3.6TB 机械盘(后续计划扩展到 10 盘位)**
- 系统**:Linux(带 **
<span class="ne-text">systemd</span>,可用 <span class="ne-text">smartctl</span>、<span class="ne-text">sensors</span> 等工具)
关注点**:**
- NAS 的主要热源在 硬盘**,CPU 负载较低且不易过热。**
- **希望在 **操作系统层面 精细控制机箱风扇(硬盘风道风扇),而不是每次进 BIOS 调整。
- 目标是:
- **风扇转速 **跟随硬盘最高温度 自动变化;
- **保持 **尽量安静 的前提下,确保硬盘温度在合理范围;
- 支持硬盘扩容**(10 盘位),脚本不绑定具体盘数量;**
- 有日志、有轮转**,行为可观测,不是黑盒;**
- 开机自启**,挂了能自动重启;**
- 不妨碍硬盘休眠**(5 分钟无访问自动进入 standby)。**
整体设计思路
设计原则
实战过程与关键步骤
一、确认主板与风扇控制芯片
查看主板信息:
sudo dmidecode -t baseboard
输出示例(关键部分):
Manufacturer: ASRock
Product Name: B250M-HDV
说明确认为 ASRock B250M-HDV 主板。
二、寻找硬件监控(hwmon)设备
先看系统中有哪些 hwmon 设备:
ls /sys/class/hwmon
for h in /sys/class/hwmon/hwmon*; do
echo "=== $h ==="
cat "$h/name" 2>/dev/null || true
ls "$h"
echo
done
初始只看到类似:
<span class="ne-text">hwmon0</span>: <span class="ne-text">nvme</span>(NVMe 硬盘温度)
<span class="ne-text">hwmon1</span>: <span class="ne-text">coretemp</span>(CPU 温度)
没有任何 <span class="ne-text">fanX_input</span> 或 <span class="ne-text">pwmX</span>,说明风扇控制芯片还没被驱动识别。
三、加载 nct6791 风扇控制芯片驱动
检查已加载模块:
lsmod | egrep "nct|it8|w836|hwmon" || echo "no related modules"
若尚未加载,尝试:
modprobe nct6775 2>/dev/null || echo "nct6775 not available"
modprobe it87 2>/dev/null || echo "it87 not available"
**在这块主板上,实际加载成功的是 **<span class="ne-text">nct6791</span> 软硬件组合,加载后再次查看:
ls /sys/class/hwmon
for h in /sys/class/hwmon/hwmon*; do
echo "=== $h ==="
cat "$h/name" 2>/dev/null || true
ls "$h"
echo
done
这时多出一项:
=== /sys/class/hwmon/hwmon2 ===
nct6791
...
fan1_input fan2_input ... fan6_input
pwm1 pwm1_enable ...
pwm4 pwm4_enable ...
pwm5 pwm5_enable ...
...
说明主板风扇/电压/温度的监控和控制接口已经暴露出来。
**为了让驱动开机自动加载,可以写入 **<span class="ne-text">/etc/modules</span>:
echo "nct6791" | sudo tee -a /etc/modules
(确认不重复即可。)
四、验证哪一路 PWM 控制哪只风扇
**进入对应 hwmon 目录(具体编号以实际为准,这里是 **<span class="ne-text">hwmon2</span>):
cd /sys/class/hwmon/hwmon2
查看当前 PWM 和风扇转速:
for x in pwm1 pwm2 pwm3 pwm4 pwm5 pwm6; do
printf "%s: " "$x"; cat "$x" 2>/dev/null || echo "(不存在)";
printf "%s_enable: " "$x"; cat "${x}_enable" 2>/dev/null || echo "(不存在)";
echo;
done
for f in fan1_input fan2_input fan3_input fan4_input fan5_input fan6_input; do
printf "%s: " "$f"; cat "$f" 2>/dev/null || echo "(不存在)";
done
示例输出(关键段):
pwm4: 76
pwm4_enable: 5
pwm5: 76
pwm5_enable: 5
fan4_input: 675
fan5_input: 683
**此时 **<span class="ne-text">pwm4/pwm5</span> 处于主板自动模式(enable=5),<span class="ne-text">fan4/fan5</span> 转速约 680 RPM。
实验验证**:将 **<span class="ne-text">pwm4/pwm5</span> 切换为手动并提高占空比(只往上调,避免停转):
cd /sys/class/hwmon/hwmon2
echo 1 > pwm4_enable
echo 1 > pwm5_enable
echo 160 > pwm4
echo 160 > pwm5
sleep 5
cat pwm4; cat pwm4_enable
cat pwm5; cat pwm5_enable
cat fan4_input; cat fan5_input
结果类似:
pwm4: 160
pwm4_enable: 1
pwm5: 160
pwm5_enable: 1
fan4_input: 1215
fan5_input: 1215
说明:
<span class="ne-text">pwm4</span> ↔ <span class="ne-text">fan4_input</span>
<span class="ne-text">pwm5</span> ↔ <span class="ne-text">fan5_input</span>
- 且两路风扇就是硬盘风道对应的风扇(转速明显上升)
五、读取硬盘温度(SMART)
列出磁盘:
lsblk -o NAME,TYPE,SIZE,MODEL
**确认机械盘设备名为 **<span class="ne-text">/dev/sda</span>、<span class="ne-text">/dev/sdb</span>、<span class="ne-text">/dev/sdc</span>、<span class="ne-text">/dev/sdd</span> 等。
**使用 **<span class="ne-text">smartctl</span> 读取 SMART 信息:
smartctl -A /dev/sda | egrep '^194|^190|Temperature'
示例温度信息:
190 Airflow_Temperature_Cel ...
194 Temperature_Celsius ... 42 (0 17 0 0 0)
整机硬盘温度概览命令:
for d in /dev/sd?; do
smartctl -A "$d" 2>/dev/null | awk "/^194 Temperature_Celsius/ {print \"$d:\", \$10\"°C\"}";
done
**我们定义 **控制指标 为:所有 <span class="ne-text">/dev/sd?</span> 中温度最高的那一块盘。
六、硬盘休眠与监控频率的配合
为了让硬盘能正常休眠(用户设置为 5 分钟无访问自动进入 standby):
- 监控间隔必须大于休眠时间**:设置 **
<span class="ne-text">INTERVAL=360</span> 秒(6 分钟),确保硬盘有完整的 5 分钟空档可以进入休眠。
- 使用
<span class="ne-text">smartctl -n standby -A</span>:如果盘已经进入 standby,该命令不会唤醒它,只是拿不到温度(视为 0)。
这样既能根据硬盘温度调风扇,又不会妨碍硬盘休眠。
核心温控脚本:按硬盘最高温调节 pwm4/pwm5(带中文日志 + 日志轮转)
脚本路径建议:<span class="ne-text">/usr/local/sbin/nas-hdd-fanctl.sh</span>
内容如下:
#!/bin/bash
LOG_FILE="/var/log/nas-hdd-fanctl.log"
MAX_SIZE=$((10 * 1024 * 1024)) # 10MB
MAX_FILES=10
lo**sg() {
# 日志滚动:大于 10MB 时,nas-hdd-fanctl.log -> .1 -> .2 ...,最多 10 个
if [ -f "$LOG_FILE" ]; then
size=$(stat -c%s "$LOG_FILE" 2>/dev/null || echo 0)
if [ "$size" -ge "$MAX_SIZE" ]; then
# 从高编号往低编号滚动
for ((i=MAX_FILES-1; i>=1; i--)); do
if [ -f "${LOG_FILE}.${i}" ]; then
if [ "$i" -eq "$MAX_FILES-1" ]; then
rm -f "${LOG_FILE}.${i}"
else
mv "${LOG_FILE}.${i}" "${LOG_FILE}.$((i+1))"
fi
fi
done
mv "$LOG_FILE" "${LOG_FILE}.1"
fi
fi
# 追加写中文日志:时间戳 + 文本
echo "$(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOG_FILE"
}
# 动态找到 nct6791 的 hwmon 目录
HWMON_DIR=""
for h in /sys/class/hwmon/hwmon*; do
if [ -f "$h/name" ] && grep -q "^nct6791$" "$h/name"; then
HWMON_DIR="$h"
break
fi
done
if [ -z "$HWMON_DIR" ]; then
lo**sg "[错误] 未找到 nct6791 的 hwmon 设备,脚本退出"
sleep 60
exit 1
fi
lo**sg "[信息] 使用 hwmon 目录: $HWMON_DIR"
PWM4="$HWMON_DIR/pwm4"
PWM5="$HWMON_DIR/pwm5"
PWM4_EN="$HWMON_DIR/pwm4_enable"
PWM5_EN="$HWMON_DIR/pwm5_enable"
# 确认 pwm4/pwm5 存在
if [ ! -f "$PWM4" ] || [ ! -f "$PWM5" ]; then
lo**sg "[错误] 在 $HWMON_DIR 下未找到 pwm4/pwm5,脚本退出"
sleep 60
exit 1
fi
# 切换 pwm4/pwm5 到手动模式
[ -f "$PWM4_EN" ] && echo 1 > "$PWM4_EN"
[ -f "$PWM5_EN" ] && echo 1 > "$PWM5_EN"
lo**sg "[信息] 已将 pwm4/pwm5 设置为手动模式"
# 初始较安静又不算太低的转速
SPEED=110
echo "$SPEED" > "$PWM4"
echo "$SPEED" > "$PWM5"
lo**sg "[信息] 初始风扇转速设为 $SPEED"
# 读取所有 /dev/sd? 的最高硬盘温度(SMART 194 Temperature_Celsius)
# 使用 -n standby:如果磁盘已休眠则不唤醒,返回空温度
get_max_hdd_temp() {
local max=0
local found=0
for d in /dev/sd?; do
[ -b "$d" ] || continue
t=$(smartctl -n standby -A "$d" 2>/dev/null | awk '/^194 Temperature_Celsius/ {print $10; exit}')
if echo "$t" | grep -q '^[0-9][0-9]*$'; then
found=1
[ "$t" -gt "$max" ] && max="$t"
fi
done
if [ "$found" -eq 0 ]; then
echo 0
else
echo "$max"
fi
}
# 获取 CPU 包温(Package id 0),整数 °C;失败则输出空
get_cpu_temp() {
sensors 2>/dev/null | awk '
/Package id 0:/ {
gsub(/[^0-9.]/,"",$4);
printf "%d", $4;
exit
}'
}
# 检测间隔(秒)——与硬盘休眠 5 分钟配合使用:这里用 360 秒(6 分钟)
INTERVAL=360
lo**sg "[信息] 开始温控循环,检测间隔 ${INTERVAL} 秒(使用 -n standby 保护硬盘休眠)"
while true; do
max_hdd=$(get_max_hdd_temp)
cpu_t=$(get_cpu_temp)
# 以"硬盘最高温"为主的风扇档位策略(偏静音)
if [ "$max_hdd" -lt 32 ]; then
SPEED=90 # 很凉,尽量安静
elif [ "$max_hdd" -lt 37 ]; then
SPEED=110 # 正常温度,轻微风
elif [ "$max_hdd" -lt 42 ]; then
SPEED=150 # 稍热,适度提高
else
SPEED=200 # 偏热,显著加强风量
fi
# CPU 高温兜底:CPU >= 70°C 强制提速
if echo "$cpu_t" | grep -q '^[0-9][0-9]*$' && [ "$cpu_t" -ge 70 ]; then
SPEED=220
fi
# 保险:限制在 [80,230]
if [ "$SPEED" -lt 80 ]; then
SPEED=80
elif [ "$SPEED" -gt 230 ]; then
SPEED=230
fi
# 应用到硬盘风道两个风扇
echo "$SPEED" > "$PWM4"
echo "$SPEED" > "$PWM5"
lo**sg "[循环] 硬盘最高温=${max_hdd}°C CPU温度=${cpu_t:-NA}°C 设定转速=${SPEED}"
sleep "$INTERVAL"
done
注意事项:
- **控制对象仅为 **
<span class="ne-text">pwm4/pwm5</span> 对应的风扇(硬盘风道),CPU 风扇依然由主板 BIOS / SmartFan 负责;
<span class="ne-text">/dev/sd?</span> 会动态适配将来扩展到 10 盘位甚至更多;
- **日志文件路径为 **
<span class="ne-text">/var/log/nas-hdd-fanctl.log</span>,单个文件 10MB 自动轮转,最多保留 10 个历史文件。
使用 systemd 配置开机自启
1. 赋予脚本执行权限
chmod +x /usr/local/sbin/nas-hdd-fanctl.sh
2. 创建 systemd 服务单元
文件路径:<span class="ne-text">/etc/systemd/system/nas-hdd-fan.service</span>
内容:
[Unit]
Description=NAS HDD temperature based fan control (pwm4/pwm5)
After=multi-user.target
Wants=multi-user.target
[Service]
Type=simple
ExecStart=/usr/local/sbin/nas-hdd-fanctl.sh
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
3. 重新加载并启用服务
systemctl daemon-reload
systemctl enable --now nas-hdd-fan.service
查看状态:
systemctl status nas-hdd-fan.service
查看日志:
tail -f /var/log/nas-hdd-fanctl.log
实际效果与可调参数
关注指标
- 硬盘温度**:通过 SMART 194 属性查看长期稳定区间(建议长期控制在 40–45°C 内)。**
- 风扇转速**:通过 **
<span class="ne-text">sensors</span> 查看 <span class="ne-text">fan4</span>、<span class="ne-text">fan5</span> 的 RPM。
- 硬盘休眠状态**:通过 **
<span class="ne-text">hdparm -C /dev/sdX</span> 查看盘是否处于 standby。
可调参数
- 检测间隔**:**
<span class="ne-text">INTERVAL=360</span>(可按需求改为 300/600 秒等,注意要大于硬盘休眠时间)。
- 档位温度阈值**:**
<span class="ne-text">32/37/42°C</span> 可根据实际盘温和噪音感受微调。
- PWM 档位**:**
<span class="ne-text">90/110/150/200/220</span> 可根据风扇特性和噪音容忍度微调。
- 日志轮转大小**:**
<span class="ne-text">MAX_SIZE=$((10 * 1024 * 1024))</span>(单文件 10MB,可按需调整)。
辅助监控脚本
**为了方便实时观察温度和转速,可以使用 **<span class="ne-text">/root/check-nas-therm.sh</span>(持续刷新版):
bash /root/check-nas-therm.sh
默认每 10 秒刷新一次,也可以自定义间隔:
INTERVAL=5 bash /root/check-nas-therm.sh
总结
这套方案主要解决了以下几个问题:
- **利用 **nct6791 芯片,在 Linux 系统层面直接接管风扇控制,不再依赖 BIOS,支持脚本化/自动化。
- **以 **硬盘最高温度 为核心指标,结合 CPU 高温兜底,实现更符合 NAS 场景的温控策略。
- 通过 中文日志 + 日志轮转**(单文件 10MB,最多 10 个)保证脚本行为可观测、可审计,便于日后优化与排障。**
- **使用 **systemd 实现 开机自启 + 异常自动重启,真正做到"设置一次,长期托管"。
- 使用
<span class="ne-text">smartctl -n standby -A</span> + 合理的监控间隔(6 分钟),确保硬盘能正常进入 5 分钟休眠,不会被监控脚本频繁唤醒。
如果你也用类似的主板/NAS 方案,可以在此基础上根据自己的硬盘温度/噪音偏好/休眠需求,微调几个关键参数,就能获得一套非常贴合自己使用习惯的自动温控系统。