來源 | 一口Linux
Linux內核需要處理并發任務,并發執行但愿對共享資源的訪問很容易導致競態。
為保證對共享資源的互斥訪問,Linux內核中提供了互斥的機制:中斷屏蔽、原子操作、互斥鎖、信號量、自旋鎖、completion
本文結合一些生活中實例,給大家講解,讓大家對這些鎖的使用有個更直觀的認識,實際工作中根據應用場景選擇合適的機制。
Linux 中斷屏蔽的核心是通過關閉 CPU 中斷響應,保證臨界區代碼原子執行。
中斷屏蔽是讓 CPU 暫時不響應指定類型中斷的機制,僅作用于當前 CPU 核心,不影響其他核心或全局中斷控制器配置。
屏蔽期間禁止睡眠或調度,否則會導致系統卡頓。 臨界區代碼必須極簡,避免長時間阻塞中斷響應。 用戶態無法直接屏蔽中斷,需通過內核模塊或系統調用間接實現。
Linux 中斷屏蔽的核心是 “暫時隔絕外界干擾,專注完成關鍵事”,生活中對應的場景都是 “做事時不被打斷,直到核心流程結束”。、
開玩王者榮耀 場景: 玩王者的時候為了不被電話打擾,可以關閉移動信號,只使用wifi聯網。 對應中斷屏蔽:手機(臨界區代碼)必須完整執行,電話呼入(中斷請求)會被暫時 “屏蔽”,避免操作被打斷影響上分。
考試時關閉手機 / 屏蔽外界干擾 場景: 期末考試時,你會把手機關機或調至飛行模式,拒絕聊天、電話等打擾,直到寫完試卷交卷。 對應中斷屏蔽:考試(臨界區代碼)必須完整執行,手機消息、他人呼喚(中斷請求)會被暫時 “屏蔽”,避免思路被打斷導致錯誤。
開車過路口時專注操作 場景: 開車經過繁忙十字路口,你會集中注意力觀察紅綠燈、行人、其他車輛,暫時不接電話、不聊微信,直到安全通過路口。 對應中斷屏蔽:“過路口” 是關鍵操作(避免交通事故),電話、消息(中斷)會被暫時忽略,確保操作不被干擾。
醫生做手術時謝絕無關打擾 場景: 外科醫生做手術時,手術室會禁止無關人員進入、不接非緊急電話,只有手術團隊的必要溝通,直到手術完成。 對應中斷屏蔽:手術(核心臨界區)是 “不可中斷” 的關鍵流程,無關干擾(普通中斷)會被屏蔽,僅保留緊急情況(如設備故障等不可屏蔽中斷)。
重要會議開啟 “免打擾模式” 場景:公司開戰略決策會,參會者會把手機調免打擾,不處理郵件、消息,專注討論決策,直到會議結束。
對應中斷屏蔽:會議討論(臨界區代碼)需要連貫思路,無關消息(中斷)被暫時屏蔽,避免決策過程被打斷。
Linux 原子操作是不可被中斷的最小執行單元,核心作用是保證多線程 / 多 CPU 核心并發訪問共享資源時的安全性,避免競態條件(Race Condition)操作要么完整執行,要么完全不執行,不會被調度或中斷打斷。
用鑰匙開門 / 鎖門 場景: 你用鑰匙擰門鎖,從 “解鎖” 到 “推門進入”(或 “鎖死” 到 “拔鑰匙”)的整個過程,不能被拆分。 對應原子性:要么完整完成 “解鎖 + 進門”(操作成功),要么沒擰開就放棄(操作失敗),不會出現 “門半開半關”(操作被打斷)的中間狀態。
掃碼支付扣款 場景: 你用手機掃碼付奶茶錢,“從你的賬戶扣 15 元” 和 “商家賬戶到賬 15 元” 是綁定的原子操作。 對應原子性:不會出現 “你扣了錢但商家沒到賬”(操作中斷),也不會 “商家到賬但你沒扣錢”(重復執行),系統會保證兩步要么一起完成,要么都不執行。
搶最后一張火車票 場景: 12306 上只剩 1 張票,你和另一個人同時點擊 “購票”,系統只會讓一個人成功。 對應原子性:“查詢余票(1 張)→ 鎖定車票 → 扣錢 → 出票” 的流程是原子的,不會被另一個人的操作打斷。
按電梯按鈕 場景: 你按電梯 “上行” 鍵,按鈕要么亮(表示電梯收到請求),要么不亮(沒按到),不會出現 “半亮半不亮” 的狀態。
銀行轉賬(完整原子流程) 場景: 從 A 賬戶轉 1000 元到 B 賬戶,核心流程是 “A 減 1000 → 驗證 A 余額是否足夠 → B 加 1000”。
Linux 互斥鎖(Mutex,全稱 Mutual Exclusion)是解決多線程 / 多進程并發訪問共享資源的核心同步機制,核心作用是 “排他性占用”—— 確保同一時間只有一個線程 / 進程能進入臨界區(操作共享資源的代碼段),其他競爭者會阻塞等待,直到鎖被釋放,從而避免競態條件和數據不一致。
Linux 互斥鎖的核心是「排他性占用 + 阻塞等待」—— 同一時間只有一個 “使用者” 能進入臨界區(共享資源),其他人必須排隊等前者用完再上,完全對應生活中 “一人使用、眾人排隊” 的場景。以下是最直觀的類比,每個場景都能對應互斥鎖的核心特性:
公共電話亭: 電話亭(臨界區)一次只能進 1 人,其他人排隊(阻塞),用完出來(解鎖)后下一人進入 —— 對應 “排他性訪問”。
衛生間門鎖: 有人使用時鎖門(加鎖),其他人在外等待(阻塞),使用完畢開鎖(解鎖)—— 對應 “解鎖者必須是加鎖者”(誰鎖的誰開)。
會議室預約系統: 會議室(共享資源)通過預約鎖(互斥鎖)分配,同一時間段只有一個團隊使用,其他團隊需等預約結束(鎖釋放)—— 對應 “長時間臨界區”(會議可能持續 1 小時,睡眠等待比自旋更高效)。
Linux 信號量是用于多線程 / 多進程同步與互斥的核心機制,本質是一個 “帶計數的同步工具”—— 通過一個整數計數器控制對共享資源的訪問權限,核心作用是:
可以類比生活中的 “電影院入場券”:影院某場次有 100 個座位(計數器 = 100),每張票對應一個座位(共享資源),觀眾憑票入場(獲取信號量),離場時回收票(釋放信號量);票賣完(計數器 = 0)后,后續觀眾需排隊等待(阻塞),直到有人離場(計數器增加)。
Linux 信號量的核心是「計數控制 + 阻塞等待」—— 用一個 “計數器” 限制同時使用資源的最大數量,沒輪到的人排隊等待,完全對應生活中 “限量使用、先到先得、排隊等候” 的場景,分 “二值信號量(互斥)” 和 “計數信號量(同步 / 限流)” 兩類類比,更易理解:
電影院場次觀影(最經典限流場景) 場景: 某場電影有 100 個座位(計數器 N=100),每張電影票對應一個 “使用名額”。 觀眾憑票入場(P 操作:計數器 - 1),滿座后(計數器 = 0),后續觀眾需在大廳排隊(阻塞等待); 有人離場(V 操作:計數器 + 1),才能放行下一位觀眾入場。
餐廳排隊叫號系統(同步 + 限流) 場景: 餐廳有 5 張餐桌(計數器 N=5),顧客到店取號(獲取信號量資格),有空桌時(計數器 > 0)叫號入座(P 操作:計數器 - 1); 用餐結束離席(V 操作:計數器 + 1),下一位顧客被叫號。若 5 張桌全滿,后續顧客需在等候區排隊(阻塞)。
共享單車停車點(共享資源 + 名額回收) 場景:小區門口有 8 個共享單車停車樁(計數器 N=8),你騎車回來時,若有空樁(計數器 > 0)可鎖車歸還(V 操作:計數器 + 1);若全滿(計數器 = 0),需去下一個停車點(或排隊等待他人取車)。其他人取車時(P 操作:計數器 - 1),空樁名額釋放。
高速公路收費站(多通道限流) 場景: 高速公路有 3 個收費通道(計數器 N=3),車輛到達后選擇空閑通道(計數器 > 0)通過(P 操作:計數器 - 1); 繳費完成后離開(V 操作:計數器 + 1)。 若 3 個通道全忙(計數器 = 0),后續車輛需在入口排隊(阻塞),直到某通道空閑。
在Linux內核中,自旋鎖(spinlock)是一種用于保護共享數據的簡單而有效的同步機制。自旋鎖允許多個處理器核心在嘗試訪問共享資源時,如果一個核心持有鎖,其他核心會“自旋”(忙等),直到鎖被釋放。這種方式避免了上下文切換的開銷,適用于鎖持有時間很短的場景。
自旋鎖的工作原理 自旋鎖通常基于原子操作實現,確保在多處理器環境中操作的原子性。在Linux內核中,自旋鎖的實現依賴于硬件提供的原子操作指令,如x86架構中的test-and-set指令或者atomic操作。
注意事項
1?) 避免死鎖?: 確保在持有自旋鎖時不會導致死鎖,例如,不要在已經持有某個鎖的情況下請求另一個鎖。
?2) 短時間持有?: 盡量保持自旋鎖的持有時間短,因為長時間的自旋會導致CPU資源浪費。
?3) 中斷上下文?: 在使用spin_lock_irq和spin_unlock_irq時,要確保在適當的上下文中使用,避免在可睡眠函數中使用這些函數。
通過合理使用自旋鎖,可以有效地管理多核CPU環境中的數據訪問沖突,提高系統的并發性能。
洗手間門外 “秒等”(最經典類比) 場景: 你急著用洗手間,走到門口發現門是關的(鎖被占用),但能聽到里面的人正在沖水、整理(明顯是 “快結束了”,臨界區極短)。 你不會下樓找其他洗手間(不會休眠 / 切換),而是在門口原地等幾秒(自旋),對方一開門你就立刻進去(搶到鎖)。
電梯口 “緊盯電梯”(短等待 + 立即響應) 場景: 你按下電梯按鈕,看到電梯顯示 “1 樓→2 樓”(即將到達,臨界區極短), 你不會去樓梯間等(不切換),而是在電梯口原地盯著指示燈(自旋檢查鎖狀態),電梯門一開就立刻進去(搶占鎖)。
便利店排隊結賬(1 人排隊 + 短等待) 場景: 你去便利店買一瓶水,結賬時前面只有 1 個人,且對方已經在掃最后一件商品(臨界區極短)。 你不會轉身去其他便利店(不切換),而是在原地等著(自旋),對方一結完賬你就立刻上前(搶到鎖)。
停車場 “繞圈等空位”(自旋 + 不放棄資源) 場景: 你開車進停車場,看到有 1 輛車正在駛出車位(臨界區極短), 你不會開車離開(不切換),而是在車位附近緩慢繞圈(自旋),對方一駛離你就立刻停車入位(搶占鎖)。
辦公室 “等打印機出紙”(短臨界區 + 自旋檢查) 場景: 你發送打印任務后,看到打印機正在打印最后 1 頁(臨界區極短), 你不會先去開會(不休眠),而是在打印機旁等(自旋),紙張一出來就立刻取走(搶到鎖)。
在Linux內核中,completion是一種同步機制,它允許一個或多個進程等待一個或多個事件的發生。 completion通常用于確保某個操作完成之前,其他進程不會繼續執行。 這在多線程編程中非常有用,特別是在需要確保數據結構在并發訪問時保持一致時。
Linux completion 的核心是「等待特定事件完成 + 完成后喚醒」—— 一個或多個線程等待某個 “目標事件” 發生,事件完成后會主動通知所有等待者,生活中對應的場景都是「等人 / 等事做完,對方完成后告知,再繼續自己的事」,核心是 “等待通知” 而非 “爭奪資源”。
朋友約飯 “等對方到齊”(最經典類比) 場景: 你和 3 個朋友約好一起吃火鍋,約定 “所有人到齊后再點菜”。你先到了火鍋店(等待線程調用 wait_for_completion),就坐著刷手機等待(阻塞休眠); 其他朋友陸續到達,最后一個朋友到店時(完成線程調用 complete),大喊 “人齊了!”(喚醒所有等待者),你們立刻開始點菜(等待線程恢復執行)。
快遞到站 “等取件通知”(單等待者場景) 場景: 你在網上買了一本書,選擇 “驛站自提”。下單后你正常工作、學習(等待線程休眠),不會每隔 5 分鐘就去驛站問(不自旋); 快遞員把書送到驛站并掃描入庫(完成線程執行 complete),驛站通過短信通知你 “快遞已到”(喚醒等待者),你收到通知后才去驛站取件(等待線程執行后續操作)。
團隊項目 “等關鍵模塊開發完成”(多等待者依賴同一事件) 場景: 公司做一個 APP,你的團隊負責 “支付模塊”,需要等 “用戶登錄模塊” 開發完成(目標事件)才能對接。你和同事們先做支付模塊的獨立開發(等待線程阻塞休眠),不浪費時間去反復詢問登錄模塊進度(不自旋); 登錄模塊開發完成后,負責人在工作群通知 “登錄模塊 OK 了”(complete_all 喚醒所有等待者),你們立刻開始對接開發(等待線程恢復)。
餐廳 “等菜上桌”(等待特定結果完成) 場景: 你在餐廳點了一份紅燒肉,下單后你坐在座位上玩手機、聊天(等待線程休眠),不會跑到后廚門口盯著廚師做菜(不自旋); 廚師做好紅燒肉后,服務員端上桌并告知 “您的菜好了”(完成線程執行 complete),你收到 “通知” 后開始吃飯(等待線程執行后續操作)。
考試 “等監考老師發卷”(統一觸發事件) 場景: 期末考試時,所有考生(多個等待線程)坐在座位上等待,不會提前翻試卷(阻塞休眠); 監考老師把試卷分發到每個座位后(完成線程執行 complete_all),宣布 “可以開始答題了”(喚醒所有等待者),所有考生同時開始考試(等待線程恢復執行)。

基于MCU的輕量級AI模型TFLite和TFLM的差別

做嵌入式項目,選RTOS要考慮哪些問題?