OpenBMC中你可能會看到std::atomic嗎?

更新 發佈閱讀 11 分鐘

對於一個很大的專案來說,我一直在思考的是「穩定性」和「強健性」這兩件事情。在我Gap Year前,專案一直面臨到一些偶發的崩潰狀態,這對我來說並不是感到太意外,只是在思考究竟哪裡應該注意而未被注意。

夢回多年前我第一次跟微軟一起在合作寫第一個LF OpenBMC的系統的時候,當時也經常發生一些崩潰的狀態,因為那時還沒有phosphor dbus interface這個Repo的誕生,也是大家剛開始從Sysinit的環境轉換到systemd的階段,對於Dbus這個東西既愛又恨。崩潰的點經常就是我們acquired了之後沒有release bus....或者是我們過於頻繁的去access dbus而不是一次把資訊讀回來後再分析。這牽涉到時間複雜度的問題,這也難怪很多大廠的考試總愛考這部分,因為...悲劇總在不經意間發生。但是有多少人能在考試的時候很強又在真實工作的時候運用出來,這我就不知道,畢竟我也覺得自己只能一直很努力的學習,怎麼學總會感到不足,漏掉...這種很想K自己一拳的時候。 (哈 但總體上我是樂觀的...尻自己一拳之後我會立刻去吃一個提拉米蘇,重新活過來)

喔好~但就像smart pointer的誕生一樣,現在記憶體的allocate與free好像已經很便利了,只要使用摩登現代C++並好好的運用,出錯的機率不高。然後我偶像之一Patrick又寫出了phosphor dbus interface之後,我感覺像是看到一本教科書,你不太需要深入去理解Dbus你只要會用就好。但這樣夠嗎?下一個OpenBMC的時代,大家會面臨的問題是什麼?為什麼系統還是有崩潰的時候?

我不確定現在還有多少的全域變數正在被使用,早期在我的記憶中,有時候我們會為了區別自己的系統與別人系統的不同而去enable不同的功能,然後就會出現不少像是"is_enabled", "activate_XXX_func"等等之類的這種痊癒變數,有些是從bitbake時的BB file中設定,再被定義到cpp code中。我們最近一起在研究的phosphor-pid-control(以下簡稱 swampd)也是一個基於 Boost.Asio 的非同步單執行緒守護process (之前看dbus-sensor的時候也提到過)。其核心邏輯圍繞著週期性的 PID 運算展開。目前還是會看到一些「全域旗標」或「共用指標」進行狀態傳遞的現象,例如 isCanceling 指標與 tuningEnabled 等全域變數。

 swampd 目前使用 bool 或 int 作為全域旗標卻沒有傳出災情是因為它運作在單執行緒的非同步事件循環中。如果是多執行緒的話,純全域變數將面臨 Data Race 的威脅。根據 C++ 標準,如果兩個執行緒同時存取同一個非原子變數,且其中至少有一個是寫入動作,該行為即為「未定義」。這可能導致:

  • 數值撕裂 (Value Tearing):在 32 位元架構存取 64 位元變數時,可能只讀到一半的修改。
  • 不可預測的狀態機崩潰:系統判定 isCanceling 為真,但相關的緩存數據卻尚未重新整理。

為什麼 std::atomic 更好?

前面寫了這麼多,只是想鋪陳最近看到別人用std::atomic這個東西,覺得新奇想跟大家分享一下而已。std::atomic 不僅僅是「執行一個原子操作」,它提供了三大核心保證:

  1. 原子性 (Atomicity):保證操作不可分割,不會讀到半成品的數值。
  2. 可見性 (Visibility):配合總線鎖或緩存一致性指令,確保修改後的數值能被其他核心偵測。
  3. 順序約束 (Ordering Constraints):透過記憶體屏障(Memory Barriers),限制編譯器與 CPU 的重排行為。

在 std::atomic 中,我們可以根據需求選擇最合適的「嚴格程度」:

  • memory_order_relaxed:僅保證原子性,不保證順序。適合單純計數(如統計數據)。
  • memory_order_acquire / memory_order_release:這是最推薦用於 swampd 這種「旗標式同步」的模式。
    • Release:確保這行寫入之前的動作全部完成。
    • Acquire:確保這行讀取之後的動作看到最新的狀態。
  • memory_order_seq_cst:預設模式,保證全域一致性順序,但開銷最大。

實際來使用看看

這個code模擬一下我們正在研究的風扇控制系統架構:有一個 Worker thread 在跑迴圈,而 Main 負責在 1 秒後下達停止指令。

#include <iostream>
#include <atomic>
#include <thread>
#include <chrono>

class FanService {
public:
// static 關鍵字代表這是類別共享的旗標,模擬專案中的 global flag
static std::atomic<bool> isCanceling;
static std::atomic<int> processedCount;

void runControlLoop() {
std::cout << "[Worker] PID 控制循環啟動...\n";
while (true) {
// --- 用法 1: memory_order_acquire (中量級) ---
// 保證看到主線程在 store 之前所做的所有記憶體修改
if (isCanceling.load(std::memory_order_acquire)) {
std::cout << "[Worker] 接收到優雅停止訊號,準備關閉...\n";
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模擬 100ms 週期

// --- 用法 2: memory_order_relaxed (輕量級) ---
// 單純累加次數,不涉及其它狀態同步,效能最高
processedCount.fetch_add(1, std::memory_order_relaxed);
}
}
};

// 初始化靜態成員
std::atomic<bool> FanService::isCanceling{false};
std::atomic<int> FanService::processedCount{0};

int main() {
FanService service;
std::thread workerThread(&FanService::runControlLoop, &service);

std::this_thread::sleep_for(std::chrono::seconds(1)); // 讓它跑一秒
std::cout << "[Main] 下達停止指令...\n";

// --- memory_order_release ---
// 確保這行之前的動作都已完成,然後才舉旗
FanService::isCanceling.store(true, std::memory_order_release);

workerThread.join();
std::cout << "[Main] 測試完成,總迭代次數:" << FanService::processedCount.load() << "\n";
return 0;
}

isCanceling 用了 Acquire/Release:因為這涉及「同步」。一旦旗標舉起,Worker 必須立刻看到主執行緒最新的變動。processedCount 用了 Relaxed:因為這只是計數,稍微晚個 1 微秒看到總數也沒關係,這樣可以省下維護記憶體順序的昂貴效能成本。

執行的command:

g++ -std=c++11 atomic_demo.cpp -o atomic_demo -pthread && ./atomic_demo

這就是為什麼 boost::asio 這麼受歡迎,因為在單執行緒下,io.run() 幫我們擋掉了大半的併發問題。但要注意,『非同步』不代表『絕對安全』。一旦工作量變大,你開始考慮使用多執行緒來跑 io.run() 時,那些散落在各處的回呼(Callbacks)就像是同時在不同車道賽車,隨時可能撞在一起。如果你還在使用傳統的全域變數,卻沒有 std::atomic 的保護,那悲劇發生的機率就會大幅增加。

你們也來跟我分享,最近還有什麼該學的摩登現代C++用法吧!畢竟我已不再年輕,而C++的進化速度總是超乎我的預期,現在C++的內容真的跟我當年讀大學第一次接觸的差好多喔!但是好像越來越棒了!跟大家分享,我最近覺得研究UNIX系統中原本就存在的command他是怎麼implement的,也是超有趣的一件事情。希望大家也會越來越喜歡嵌入式的世界和領域喔!我們一起加油吧!

留言
avatar-img
L'Angolo di Embedded
23會員
26內容數
這裡會有一些我對於OpenBMC, Embedded Software的學習與經驗分享, 本來只在Line社群跟大家互動, 但是有夥伴提出想要看到歷史文章的需求, 於是我決定把它放到這裡, 努力磨練自己的技術和文筆。
L'Angolo di Embedded 的其他內容
2026/01/19
你知道在OpenBMC裡面要怎麼把時間處理好嗎?不知道的話...進來看看吧!
2026/01/19
你知道在OpenBMC裡面要怎麼把時間處理好嗎?不知道的話...進來看看吧!
2026/01/12
探討伺服器風扇分區控制(Zone)的重要性,並更近一步介紹DbusPidZone 類別的核心職責,包括 D-Bus 通訊、感測器快取與管理、PID 迴路協調以及 Failsafe 模式。
2026/01/12
探討伺服器風扇分區控制(Zone)的重要性,並更近一步介紹DbusPidZone 類別的核心職責,包括 D-Bus 通訊、感測器快取與管理、PID 迴路協調以及 Failsafe 模式。
2026/01/05
以風扇控制為例,說明 OpenBMC 如何透過「策略與實作分離」的設計,將不同產品間的硬體差異有效收斂。從以 JSON 描述散熱拓撲與 PID 策略,到以介面抽象硬體互動,OpenBMC 避免了 hard code 與 ifdef 的擴散,展現其作為可擴充、可維護框架的核心價值。
2026/01/05
以風扇控制為例,說明 OpenBMC 如何透過「策略與實作分離」的設計,將不同產品間的硬體差異有效收斂。從以 JSON 描述散熱拓撲與 PID 策略,到以介面抽象硬體互動,OpenBMC 避免了 hard code 與 ifdef 的擴散,展現其作為可擴充、可維護框架的核心價值。
看更多
你可能也想看
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
熱騰騰的文章又來囉~ 在開始之前想先聊聊為甚麼我想些 picoCTF 這系列的文章。 St
Thumbnail
熱騰騰的文章又來囉~ 在開始之前想先聊聊為甚麼我想些 picoCTF 這系列的文章。 St
Thumbnail
CSS 的繼承性是開發網頁樣式時的一個重要概念,它使得樣式設計更加靈活和高效,有助於提高程式碼的可讀性、一致性和可重用性,並加快開發速度,從而提供更好的開發體驗。
Thumbnail
CSS 的繼承性是開發網頁樣式時的一個重要概念,它使得樣式設計更加靈活和高效,有助於提高程式碼的可讀性、一致性和可重用性,並加快開發速度,從而提供更好的開發體驗。
Thumbnail
軟體系統的發展歷程大多相似,首重解決基本需求、提供操作介面,進而提升安全性、擴充功能、優化操作。
Thumbnail
軟體系統的發展歷程大多相似,首重解決基本需求、提供操作介面,進而提升安全性、擴充功能、優化操作。
Thumbnail
討論系統架構時,我們常忽略低流量時期的準備,但真正的挑戰在於怎樣在突發高流量時保持穩定。我們深入探討了如何透過水平擴展、負載均衡、快取策略等多維度規劃,來強化系統對高流量的承受力,確保系統的靈活擴展與高可用性。
Thumbnail
討論系統架構時,我們常忽略低流量時期的準備,但真正的挑戰在於怎樣在突發高流量時保持穩定。我們深入探討了如何透過水平擴展、負載均衡、快取策略等多維度規劃,來強化系統對高流量的承受力,確保系統的靈活擴展與高可用性。
Thumbnail
確保沒有遺漏或錯誤 程式的完整資訊資料對於程式設計至關重要。這是因為只有透過完整的資訊,我們才能確保在程式設計中沒有任何遺漏或錯誤。最終,後台管理扮演著管理系統中所有動作和行為是否符合特定標準的重要角色。 採取不符合預期的行動 這種符合性的重要性在於,當我們設計程式時,希望使用者按照預期的方式
Thumbnail
確保沒有遺漏或錯誤 程式的完整資訊資料對於程式設計至關重要。這是因為只有透過完整的資訊,我們才能確保在程式設計中沒有任何遺漏或錯誤。最終,後台管理扮演著管理系統中所有動作和行為是否符合特定標準的重要角色。 採取不符合預期的行動 這種符合性的重要性在於,當我們設計程式時,希望使用者按照預期的方式
Thumbnail
資料的統合 在程式設計中,其他人通常關心是否注意到執行的細節。作為程式設計師,主要應該關心的是程式的表現,但往往忽略了很多細節,這些細節可以決定程式的好壞。程式的好壞很大程度上取決於資料的統合,也就是資料是否被正規化。 不同類型的資料在系統中呈現一致 正規化可能對一些人來說聽起來很抽象,有些人
Thumbnail
資料的統合 在程式設計中,其他人通常關心是否注意到執行的細節。作為程式設計師,主要應該關心的是程式的表現,但往往忽略了很多細節,這些細節可以決定程式的好壞。程式的好壞很大程度上取決於資料的統合,也就是資料是否被正規化。 不同類型的資料在系統中呈現一致 正規化可能對一些人來說聽起來很抽象,有些人
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News