CPU 燒到 97 度,風扇只轉 3600 轉,因為出廠設定叫它別太吵
MSI 筆電在 Ubuntu 上沒有 MSI Center,效能設定檔就停在出廠的 comfort,風扇上限被壓在最高轉速的 57%。我做了一支 Tauri 2 的小程式去切它,那次量到 CPU 從 96 度掉到 68 度(一次觀察,不是規格,溫度會隨負載大幅移動)。

這篇講的東西
- 原始碼與安裝步驟:https://github.com/yazelin/msi-fan
- 實際控制靠核心模組 msi-ec,這支只是介面
同一台機器,只差在 EC 裡的一個設定檔:
| comfort | turbo | |
|---|---|---|
| 風扇 | 3555 到 3609 RPM | 6315 RPM |
| CPU | 95 到 97°C | 66 到 71°C |
| GPU | 67 到 68°C | 63 到 65°C |
這張表要配一段但書,而且我後來發現它有一欄是錯的。
先講但書:我當時沒記錄跑的是什麼負載、量了多久,所以它是一次觀察,不是規格。溫度那兩列會隨負載大幅移動。發文幾天後我撞到一個現成的例子,turbo、風扇 6315 轉,CPU 卻是 95 度,因為背景有二十個失控的檔案索引行程把 load average 頂到 161。把它們清掉之後,同樣的風扇轉速,CPU 掉到 76 度。風扇沒變,差 19 度。所以看到上表的 66 度不要當成「切到 turbo 就會這樣」。
錯的那一欄是 turbo 的 6315 RPM。6315 是 Cooler Boost 的全速,不是 turbo 曲線的上限,我量那次一定是 Boost 也開著,只是當時沒記下來。後來我把三種狀態分開重量了一次:
| 狀態 | 溫度 | 風扇 |
|---|---|---|
| turbo,閒置 | 62 到 66°C | 3000 RPM(八次取樣完全不動) |
| turbo,滿載 | 95°C | 4102 到 4571 RPM |
| turbo,放開負載 | 77 → 70°C | 4444 → 4102 RPM |
| Cooler Boost 開 | 不看溫度 | 6233 到 6315 RPM |
| eco,66 到 67°C | 2651 到 2711 RPM |
所以真相是:風扇確實跟著溫度跑,升溫往上、降溫往下,反應大約十幾秒。但 turbo 的曲線很保守,95 度也只吹到 4571 轉,而這顆風扇明明轉得到 6315,等於熱到燙手還留著將近三成餘力不用。
comfort 更誇張,上限壓在 3600 轉,也就是最高轉速的 57%,溫度再高也就這樣,然後 CPU 自己降頻。這就是我要那個 turbo 開關的理由,也是為什麼「自動開 Boost」這個功能有存在的必要:曲線自己壓不住的時候,得有人踩到底。
改那個設定檔的官方工具是 MSI Center,只有 Windows 版。核心內建的 msi_wmi_platform 只能讀不能寫。這支程式就是補上那個缺口。
先講不能做什麼
我把這張表放在 README 最前面,免得有人期待落空:
| 功能 | 可行 |
|---|---|
| 即時看 CPU/GPU 溫度與風扇 RPM | 可以 |
| 切換效能模式(turbo/comfort/eco) | 可以 |
| Cooler Boost 開關 | 可以 |
| 依溫度自動開關 Boost | 可以 |
| 拖滑桿指定轉速,例如 4200 RPM | 不行 |
| 自訂溫度與轉速的曲線 | 不行 |
後兩項不是還沒做,是硬體沒開放。msi-ec 在我這台(Cyborg 14 A13VF,EC 版本 14P1IMS1.106)的設定檔只暴露三個開關,沒有曲線控制點。available_fan_modes 裡雖然列了 advanced,但對應的曲線欄位沒有被暴露出來。
先解釋一下 Cooler Boost 是什麼,因為它跟效能模式常被混在一起。它是 MSI 的「風扇無條件全速」開關,不看溫度,開了就一路吵到你關掉。 turbo 那種效能模式是把風扇曲線的上限拉高、風扇仍然自動溫控;Cooler Boost 沒有曲線,就是踩到底。
所以這裡的「自動控制」是拿 Boost 這個開關做的,不是連續調速。我覺得這種邊界一定要寫在最前面,不然使用者裝完打開,第一個動作就是找那根滑桿。
開關型的自動控制要有遲滯區
只有開關就得處理一個很基本的問題:溫度在門檻附近抖動的時候,風扇會每秒開開關關,比不做還糟。
所以規則是兩個門檻:超過上門檻持續 N 秒就開 Boost,掉到下門檻以下持續 N 秒才關,中間那段是遲滯區,維持現狀不動作。off_below 必須低於 on_above,寫反了程式會擋下來。
這是硬體控制跟純軟體最不一樣的地方。理想模型裡溫度是一條平滑曲線,實際上它一直在抖,而每一次抖動都會變成一次真實的機械動作。
安裝路上的三個坑
第一個是機型白名單。msi-ec 是靠 EC 韌體版本字串比對的,而且是完整比對不是前綴,版本差一號就不會載入。所以裝之前要先把自己的版本字串挖出來(ec_sys 加 dd 讀 offset 160 那 16 個 byte),拿去 msi-ec.c 裡搜,有才裝。
第二個是權限。shift_mode 那三個 sysfs 檔案預設 root-only。我原本用 udev 規則處理,但 RUN+= 對 sysfs 屬性不太穩定,改成一支 systemd 一次性服務,可靠也好除錯。
配套的坑是:加入群組之後要重新登入才生效,而在那之前,檔案看起來是可寫的,但你的 process 其實不帶那個群組。程式會偵測到這個狀態並在畫面上講出來,不然使用者只會看到「按了沒反應」。
第三個很小但很煩:install.sh 覆蓋執行檔之前要先停掉背景守護行程,不然會拿到 Text file busy。
為什麼是 Tauri 2
因為它就該是一個小視窗,開著看溫度、按兩下切模式,然後關掉。Rust 那邊讀寫 sysfs,前端就是一個面板。背景自動控制拆成 user systemd service(msi-fan-auto),關掉視窗也繼續管。
8 月 8 日還做了一件事:把效能模式跟自動 Boost 解耦。原本「套用效能模式」被綁在自動控制的 enabled 底下,等於想要開機就 turbo 的人,被迫連自動 Boost 一起開,而那是兩件事。現在守護行程啟動時一律套用設定檔裡的模式,enabled 只管 Boost 規則;GUI 按模式鈕也會寫回設定檔,上次選的就是下次開機套用的。
後記:那個「自動控制」開關一直在騙人
8 月 10 日我在自己這台上發現,整支程式最重要的功能從來沒有真的運作過。
當時 CPU 在 95 度待了 45 秒,門檻設的是 85 度、持續 10 秒,Boost 卻動都不動。查下去才知道:GUI 那個「自動控制」開關只把 enabled: true 寫進設定檔,不會去啟動 systemd 服務。而真正在依溫度開關 Boost 的是那支背景守護行程。所以介面顯示開著、設定檔也寫著開著,實際上沒有任何東西在監控溫度,而使用者從畫面上完全看不出來。
這比功能沒做還糟。沒做的功能使用者知道要自己顧,假裝有在跑的功能會讓人放心去燒 CPU。
修法是三件事:開關真的去 systemctl --user enable --now/disable --now;UI 讀的是服務的實際狀態,不是設定檔;兩者不一致時紅字警告並附上修復指令。footer 也加了一行「自動控制執行中/未執行」,讓它隨時看得到。
驗證不是我測出來的,是真實事件驗的:修完之後我另一條工作線在跑 ffmpeg 切影片,CPU 衝過 85 度,服務自動開了 Boost、風扇上到 6315 轉;影片切完溫度掉到 72 度以下持續 10 秒,它自己關掉。全程沒有人介入。
同一輪還修掉三個一直在說謊的顯示。最嚴重的是風扇那個百分比:realtime_fan_speed 我一直當成百分比顯示還畫了進度條,實際上它是 EC 的原始 byte(ec_read(0x71) 直接吐出來,驅動沒有任何換算)。實測開關 Boost 讓 RPM 在 6233 到 2944 之間差了 2.4 倍,這個值紋風不動;切 turbo 或 eco 也不動;只有溫度跨過界線才跳一階。它是曲線的階,不是風扇的出力。所以最糟的情況就是風扇全速狂轉、UI 卻顯示 50%。
另外兩個:兩顆風扇原本被 Math.max() 合併成一個數字,有一顆停轉根本看不出來,改成分開列;還有加上 SSD 溫度、load average 跟獨顯使用率,因為那天讓 CPU 燒到 95 度的其實是二十個失控的檔案索引行程,一個只顯示溫度的介面會讓人一直往散熱的方向找。
範圍、那張「不能做什麼」的表、遲滯區的設計,是我定的;Rust 跟前端的程式碼是 AI 寫的。硬體的部分沒得談,msi-ec 暴露什麼就只能用什麼。
如果你也是 MSI 加 Ubuntu,而且覺得筆電熱得莫名其妙,先去查一下自己的 shift_mode 現在是什麼。很可能它從你買回來那天就沒動過。