OpenBMC 風扇控制的設計:從 JSON 設定到介面抽象的認識

更新 發佈閱讀 11 分鐘

之前到各個地方去聊到OpenBMC會是一個適用於各個產品的codebase,都會收到大家以困惑的表情回饋給我。停頓了0.99秒之後,我就會立刻反應過來,其中一個讓人難以認同的關鍵原因在於——即使服務的運算邏輯或方法可以共用,不同產品之間的「參數」卻往往完全不同。如果這些參數不能 hard code/ifdef 在程式裡,那到底該怎麼設定?差異這麼多,難道不會讓程式碼變得支離破碎嗎? 這些不可避免的產品差異,究竟要如何在程式設計上被妥善收斂,而不是一路擴散成維護上的負擔?

這個質疑非常合理。如果我們還用傳統的嵌入式開發思維,把所有東西都寫死在程式碼裡,那 OpenBMC 充其量只是一堆零散函式庫的集合,稱不上是一個「框架」。

但 OpenBMC 的核心價值,恰恰就在於解決這個問題。它的設計哲學是「策略與實作分離」。今天,我就以風扇控制服務 phosphor-pid-control 為例,和大家聊聊,程式碼裡的差異性是如何「收斂」的,以及我們該如何優雅地管理不同產品間的設定。

「策略與實作分離」

第一層收斂:用 JSON 定義「硬體拓撲」與「散熱策略」

首先,最核心的差異——也就是每個產品的硬體配置和 PID 控制參數——是完全從主程式中抽離的。phosphor-pid-control 在啟動時,tryRestartControlLoops()會去尋找一個名為 config.json 的設定檔。

這個 JSON 檔案,就是一份「系統散熱藍圖」。它用結構化的方式描述了:

  1. 有哪些感測器 (Sensors):定義了溫度感測器的名稱、在 DBus 上的路徑、讀取方式(主動監聽或被動讀取)、甚至超時時間。
  2. 有哪些散熱區域 (Zones):將感測器和風扇進行分組。例如,CPU 區域可能由 CPU_Temp 和 DIMM_Temp 感測器觸發,並控制 FAN_0 和 FAN_1。機箱區域則可能由 Ambient_Temp 控制所有的風扇。
  3. 每個區域的控制演算法 (PIDs):這就是策略的核心。你可以為每個區域定義一或多個 PID 控制器。每個控制器可以有不同的輸入感測器、不同的 PID 參數(P、I、D 係數)、不同的目標溫度 (Setpoint),以及不同的風扇轉速輸出範圍。

這意味著,我們的 A 產品和 B 產品,就算散熱設計截然不同,但它們可以執行完全相同的 phosphor-pid-control 執行檔。我們所要做的,僅僅是在各自的韌體映像檔中,提供一份專屬於它們的 config.json

第二層收斂:用「介面」抽象化「硬體互動」

好,現在我們把硬體配置和參數抽離了。但下一個問題來了:讀取感測器的方式可能不同。有些感測器是透過 hwmon 的 sysfs 檔案節點讀取,有些則是其他 DBus 服務提供的屬性。寫入風扇 PWM 的方式也可能不同。難道這些都要在主邏輯裡用 if-else 來判斷嗎?(NO~~~

在 phosphor-pid-control 的程式碼裡,你會看到一個關鍵檔案 interfaces.hpp。它定義了兩個非常重要的抽象基礎類別 (Abstract Base Class):

  • ReadInterface:它只定義了一個純虛擬函式 read()。任何東西只要能提供一個溫度讀值,就可以繼承它。
  • WriteInterface:它定義了 write()。任何東西只要能接收一個值來設定轉速,就可以繼承它。

PID 演算法的核心邏輯,操作的對象從來不是一個具體的「DBus 感測器」或「Sysfs 風扇」,而僅僅是 ReadInterface 和 WriteInterface。這就像一份合約:PID 演算法說:「我保證我只會呼叫 read() 和 write()」,而感測器實作類別則說:「我保證我會提供 read() 和 write() 的具體實作」。

所以,當我們在 JSON 中定義一個感測器類型是 dbus 時,工廠函式就會建立一個 DbusSensor 物件;當類型是 sysfs 時,就建立一個 SysfsSensor 物件。但對 PID 迴圈來說,它們都只是 ReadInterface,一視同仁。

這帶來了巨大的擴充性。如果明天我們有一個新的感測器是透過 I2C 讀值的,我們需要做什麼?不是去修改 PID 主迴圈,而是去新增一個 I2cSensor 類別,實作那個 read() 介面。核心程式碼,完全不用動。這就是第二層收斂,它將「如何做事」的細節完美地封裝了起來。

小番外篇:interfaces.hpp 可說是風扇控制的靈魂

SOLID 是由 Robert C. Martin (Uncle Bob) 提出的五大物件導向設計原則的縮寫。這些原則的目的只有一個:對抗軟體腐化。所謂的軟體腐化,指的就是程式碼隨著時間推移,變得難以測試、牽一髮而動全身(僵化)、以及無法重用(黏滯)。

interfaces.hpp 這個檔案可以說是 phosphor-pid-control 專案的靈魂,它完美體現了「依賴反轉原則 (Dependency Inversion Principle)」和「介面隔離原則 (Interface Segregation Principle)」。

依賴反轉原則 (Dependency Inversion Principle, DIP)

——「高層模組不應該依賴低層模組,兩者都應該依賴於抽象。」

在傳統的嵌入式開發思維中,我們很容易寫出直觀但耦合度極高的程式碼。例如,PID 控制演算法(高層策略)需要讀取溫度,我們可能會直接在演算法裡呼叫 read_i2c_register(...) 或 dbus_get_property(...)(低層機制)。

這種寫法看似直接,卻埋下了巨大的隱患:PID 演算法被「綁死」在特定的硬體或通訊協定上了

  • 如果你想在另一個使用 SPI 介面讀取溫度的平台上重用這套 PID 演算法?做不到,因為程式碼裡寫死了 I2C。
  • 如果你想在電腦上跑單元測試,驗證 PID 邏輯是否正確?做不到,因為你的電腦沒有那個 I2C 暫存器。

這就是 DIP 要解決的問題。在 interfaces.hpp 中,我們定義了 ReadInterface

class ReadInterface {
virtual ReadReturn read(void) = 0;
};

這就是所謂的「抽象」。現在,PID 演算法不再依賴「I2C」或「DBus」,它只依賴 ReadInterface

  • PID 演算法:「不管你是誰,只要你能實作 read() 給我數值就好。」
  • 具體的感測器(如 DbusPassiveSensor):「我實作了 ReadInterface,我負責去 DBus 撈資料。」

依賴關係被反轉了。原本是「演算法」依賴「硬體」,現在是「演算法」與「硬體」都依賴於 interfaces.hpp 定義的這個介面。這讓 phosphor-pid-control 能夠以同一套邏輯核心,透過抽換不同的 ReadInterface 實作,輕鬆適應 OpenBMC 支援的成千上萬種硬體平台。

介面隔離原則 (Interface Segregation Principle, ISP)

——「客戶端不應該被迫依賴它們不使用的方法。」

ISP 強調的是介面的純粹性精確性。試想一下,如果我們為了圖方便,定義了一個包山包海的 HardwareDevice 介面:

// 錯誤示範:臃腫的介面
class HardwareDevice {
virtual double readTemperature() = 0;
virtual void setFanSpeed(double pwm) = 0;
};

對於一個單純的「溫度感測器」來說,它只負責讀值,根本無法設定轉速。但因為繼承了這個臃腫的介面,它被迫要實作 setFanSpeed(),可能只能在裡面留空或拋出例外。這不僅讓程式碼變得醜陋,更讓呼叫者感到困惑:「為什麼我可以對溫度計設定轉速?」

回到 interfaces.hpp,你會發現設計者非常刻意地將讀取與寫入拆分開來:

  • ReadInterface:專注於「讀取」。任何能產生數值的東西(溫度計、電壓計、功率計)都實作它。
  • WriteInterface:專注於「寫入」。任何能接受控制訊號的東西(風扇 PWM、水泵轉速)都實作它。

這就是 ISP 的精髓。PID 控制器在蒐集輸入數據時,只索取 ReadInterface;在輸出控制訊號時,只索取 WriteInterface。這種設計讓系統中的每個組件都保持輕量、職責單一,且互不干擾。回到 interfaces.hpp,你會發現設計者非常刻意地將讀取與寫入拆分開來:

  • ReadInterface:專注於「讀取」。任何能產生數值的東西(溫度計、電壓計、功率計)都實作它。
  • WriteInterface:專注於「寫入」。任何能接受控制訊號的東西(風扇 PWM、水泵轉速)都實作它。

這就是 ISP 的精髓。PID 控制器在蒐集輸入數據時,只索取 ReadInterface;在輸出控制訊號時,只索取 WriteInterface。這種設計讓系統中的每個組件都保持輕量、職責單一,且互不干擾。

以上這些設計帶來了三大好處:

  1. 可擴充性 (Extensibility):當需要支援新的感測器或風扇控制器時,我們只需要新增一個實作這些介面的新類別,而完全不需要修改核心的 PID 演算法。
  2. 可測試性 (Testability):在進行單元測試時,我們可以輕易地建立一個「假的」(Mock) 感測器或風扇物件來模擬各種硬體行為(例如,溫度急遽上升、感測器讀取失敗),從而驗證 PID 演算法的反應是否正確。
  3. 可維護性 (Maintainability):程式碼的職責被清晰地劃分開來。負責 PID 演算法的工程師和負責硬體驅動的工程師可以專注於各自的領域,降低了程式碼的耦合度。

所以接下來閱讀 interfaces.hpp 時,請不要只把它看作是一堆 C++ 的 class 和 struct 定義。它是整個 phosphor-pid-control 專案的架構樞紐


留言
avatar-img
L'Angolo di Embedded
22會員
26內容數
這裡會有一些我對於OpenBMC, Embedded Software的學習與經驗分享, 本來只在Line社群跟大家互動, 但是有夥伴提出想要看到歷史文章的需求, 於是我決定把它放到這裡, 努力磨練自己的技術和文筆。
L'Angolo di Embedded 的其他內容
2025/12/22
本文以 BCM2711 datasheet 為核心,說明如何從硬體控制器層理解 Raspberry Pi,協助工程師將規格文件轉化為可用於系統分析與除錯的依據。
2025/12/22
本文以 BCM2711 datasheet 為核心,說明如何從硬體控制器層理解 Raspberry Pi,協助工程師將規格文件轉化為可用於系統分析與除錯的依據。
2025/12/08
這篇文章會帶你從「嵌入式系統」的概念開始,理解 Raspberry Pi 與 Arduino 的角色、能力、差異,並一步步從你提供的資料中拼接出完整面貌,讓你能真正踏入 embedded 的世界。
2025/12/08
這篇文章會帶你從「嵌入式系統」的概念開始,理解 Raspberry Pi 與 Arduino 的角色、能力、差異,並一步步從你提供的資料中拼接出完整面貌,讓你能真正踏入 embedded 的世界。
看更多
你可能也想看
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
我們似乎都曾有過那種「越努力,越焦慮」的學習經驗,投入了大量心力卻總在原地踏步。羊羹我長期以來也在思考這個問題,後來才發現,關鍵或許不在於努力的多寡,而是我們是否真正理解大腦這套學習系統的運作規則。
Thumbnail
我們似乎都曾有過那種「越努力,越焦慮」的學習經驗,投入了大量心力卻總在原地踏步。羊羹我長期以來也在思考這個問題,後來才發現,關鍵或許不在於努力的多寡,而是我們是否真正理解大腦這套學習系統的運作規則。
Thumbnail
書書的創作挑戰 Day 4:我想療癒誰? 這篇文章是我開始創作以來最深的一次聚焦。 我想療癒的對象,是那些還沒遇見「溫柔開始」的人。 不是要你立刻變好,而是陪你慢慢靠近真正的自己。 從命盤到真心,我把這條路走過一次,現在想邀請你一起來走一遍。
Thumbnail
書書的創作挑戰 Day 4:我想療癒誰? 這篇文章是我開始創作以來最深的一次聚焦。 我想療癒的對象,是那些還沒遇見「溫柔開始」的人。 不是要你立刻變好,而是陪你慢慢靠近真正的自己。 從命盤到真心,我把這條路走過一次,現在想邀請你一起來走一遍。
Thumbnail
在高溫高濕環境下,當人體無法有效調節體溫時,可能會熱痙攣、暈厥甚至會導致衰竭的中暑。在潮濕悶熱的氣候,如何預防熱傷害、警覺疑似熱傷害的症狀以及發生熱傷害時的處置可以怎麼做,一起來認識一下吧!
Thumbnail
在高溫高濕環境下,當人體無法有效調節體溫時,可能會熱痙攣、暈厥甚至會導致衰竭的中暑。在潮濕悶熱的氣候,如何預防熱傷害、警覺疑似熱傷害的症狀以及發生熱傷害時的處置可以怎麼做,一起來認識一下吧!
Thumbnail
今年又被邀約至碩專班做專題演講,我還是想玩點新花樣,畢竟都過了一年如果還是講同樣的題目,那不就代表自己這一年來都沒什麼進步嘛。一般來說針對聽眾的背景差異,我在選擇演講題目時的傾向性會比較明顯;像在職專班都是業界人士,我就會思考比較專業與深入的主題。
Thumbnail
今年又被邀約至碩專班做專題演講,我還是想玩點新花樣,畢竟都過了一年如果還是講同樣的題目,那不就代表自己這一年來都沒什麼進步嘛。一般來說針對聽眾的背景差異,我在選擇演講題目時的傾向性會比較明顯;像在職專班都是業界人士,我就會思考比較專業與深入的主題。
Thumbnail
透過提出了諸多工業設計的可能性,讓對於工業設計領域及範疇比較陌生的人,藉由認識、釐清等不同範疇的設計內容及目標,建立起對於廣義的工業設計學科方向有基本的認識。分辨何者為應該將投入心力的對象,並設定合理清晰的目標,避免被不必要的建議及期待干擾,相信設計的成果會是豐富而準確的。
Thumbnail
透過提出了諸多工業設計的可能性,讓對於工業設計領域及範疇比較陌生的人,藉由認識、釐清等不同範疇的設計內容及目標,建立起對於廣義的工業設計學科方向有基本的認識。分辨何者為應該將投入心力的對象,並設定合理清晰的目標,避免被不必要的建議及期待干擾,相信設計的成果會是豐富而準確的。
Thumbnail
探討《學矽谷人做身體駭客》一書,強調神經語言程式學在改善行為表現上的應用,特別是透過次感元進行心理排練。作者反思了對次感元概念的理解,並以民事訴訟中規則的重要性為例,說明系統性建構的價值。文章最後強調理論與實踐相結合的重要性。
Thumbnail
探討《學矽谷人做身體駭客》一書,強調神經語言程式學在改善行為表現上的應用,特別是透過次感元進行心理排練。作者反思了對次感元概念的理解,並以民事訴訟中規則的重要性為例,說明系統性建構的價值。文章最後強調理論與實踐相結合的重要性。
Thumbnail
節目這邊聽 📎錄製時間: 2023.07 各種雇主品牌獎項到底有什麼不同?拿到獎項就是好公司? 今天邀請人力資源部的Jean來分享雇主品牌是什麼? 對求職者、對公司有什麼幫助呢? 小編忙到每個人都來問,是不是需要找新人幫忙~ 不是小編不願意找人,是適任的好員工真的很難找啊!(仰天吶喊 但該不會這
Thumbnail
節目這邊聽 📎錄製時間: 2023.07 各種雇主品牌獎項到底有什麼不同?拿到獎項就是好公司? 今天邀請人力資源部的Jean來分享雇主品牌是什麼? 對求職者、對公司有什麼幫助呢? 小編忙到每個人都來問,是不是需要找新人幫忙~ 不是小編不願意找人,是適任的好員工真的很難找啊!(仰天吶喊 但該不會這
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News