目錄
1.問題背景:
2.原子操作
2.1 硬件操作
2.1.1 LDREX/LDXR指令
2.1.2 STREX/STXR指令
2.2 軟件操作
2.3 軟件硬件操作的各性能對比
3.總結
1.問題背景:
我們知道,RTOS的任務調度算法是搶占式優先級調度算法。
既然是搶占了,說明會出現一種情況:當我們的任務還沒完成的時候,CPU的權限就被更高優先級的任務給搶去了。
有的時候,我們任務還在進行重要數據的運算,然后這個數據的內存是和其他任務共享的,在這互相搶占中,就會出現數據運算的丟失。
既然我都說重要了,說明這件事情是不可容忍的!
那么我們有什么辦法解決呢?
有的,兄弟有的,那就是原子操作。
2.原子操作
首先我們要知道什么是原子操作:
原子操作指的是在執行過程中不可被中斷的操作。這意味著一旦這個操作開始執行,它就會持續執行直至完成,期間不會被其他線程或進程打斷。
就是你不管在什么時候查詢他的狀態,他的狀態有且只有兩個,完成和未完成。
那這么厲害的東西,是怎么實現的呢?
目前主流的原子操作的實現方式是通過硬件實現的,少部分不支持硬件的MCU是通過軟件實現的。
2.1 硬件操作
就像中斷一樣,原子操作也是通過硬件支持的,而硬件這么支持原子操作呢?我們可以查看ARM(Armv6)的手冊。ARM Synchronization Primitives Development Article
得知我們原子操作和中斷一樣在匯編層提供了硬件指令集:LDREX和STREX
提示:
- 硬件支持就是指硬件對我們功能的實現提供了什么幫助,而這個幫助這么用到的呢?就是通過在軟件層調用硬件指令,后續代碼的實現就在硬件上不占用軟件的資源。
- 軟件支持就是軟件開辟一個臨界區,你的代碼實現是在軟件上,占用的是軟件的資源。
- 在ARMv8指令集下,LDREX指令被改名成了LDXR指令,而STREX指令被改名成了STXR指令,功能基本上是一樣的,除了添加了一個新的特性。當全局監視器標記的對某段內存的獨占訪問被清空后,將向所有標記了對該段內存獨占訪問的CPU核都發送事件,將它們從WFE指令中喚醒,繼續執行。
下面我們查看手冊中關鍵的部分。
2.1.1 LDREX/LDXR指令
LDREX 指令從內存中加載一個字節,并將獨占監視器的狀態初始化為用于跟蹤同步操作的值。
LDREX R1,[R0]
會從 R0 中的地址處取出一個值(無法被打斷),將值存入 R1 中,并更新獨占監視器。
2.1.2 STREX/STXR指令
STREX 指令會對一個字進行條件存儲到內存中。
如果獨占監視器允許進行此存儲操作,那么該操作會更新內存中的位置,并在目標寄存器中返回值 0,表示操作成功。
如果獨占監視器不允許進行此存儲操作,那么該操作不會更新內存位置,并在目標寄存器中返回值 1。
這使得能夠基于內存操作的成功或失敗來實現條件執行路徑。
STREX R2, R1, [R0]
執行到 R0 中地址的存儲獨占操作,條件存儲 R1 中的值,并在 R2 中指示成功或失敗。
當然這個內存獨占訪問還是能繼續寫下去的,但這樣的話篇幅過長脫離了我們的主題,且閱讀難度驟升,因此本章先忽略,各位感興趣的可以自行去其他博客查看。
2.2 軟件操作
軟件操作最終實現的結果肯定是和硬件相同的,原理就是我們軟件算法和同步機制模擬原子性。依賴軟件的鎖/信號量和算法的實現。下面舉一個例子。
pthread_mutex_lock(&mutex); // 加鎖 // 執行原子操作 pthread_mutex_unlock(&mutex); // 解鎖
注意:
如果在硬件支持的情況下,鎖的實現是通過硬件指令實現的。
但如果沒有硬件呢?那就是通過一些輪詢或者算法來實現,例如:
- 忙等待(Busy Waiting):也稱為自旋鎖(Spinlock)。一個線程不斷地檢查鎖是否可用,直到成功獲取鎖為止。這種方法簡單但效率低下,因為它會占用大量的CPU時間。
- Peterson算法:一種經典的軟件解決方案,用于在兩個線程之間實現互斥。它利用了共享內存和幾個布爾標志位以及一個指示哪個線程準備進入臨界區的變量。
- Lamport's bakery algorithm:為了解決多個進程之間的互斥問題而設計的一種算法,它模擬了一個“面包店”取號排隊的過程,確保每個“顧客”(進程)按照先來后到的順序獲得服務(進入臨界區)。
2.3 軟件硬件操作的各性能對比
對比維度 | 硬件實現 | 軟件實現 |
性能 | 高(直接由硬件指令完成) | 低(依賴鎖或算法,存在上下文切換) |
兼容性 | 依賴硬件支持(如x86、ARM指令集) | 通用性強,適用于任何硬件平臺 |
實現復雜度 | 低(由編譯器/庫自動調用硬件指令) | 高(需手動實現鎖或算法) |
適用場景 | 高性能并發編程(如數據庫、操作系統) | 資源受限或硬件不支持的場景 |
3.總結
在項目越來越復雜,MCU的性能、項目對性能的要求越來越高的時候,單單一個裸機跑循環實現各種任務的優勢會大大縮減,大部分都是通過各種RTOS這種搶占式任務的創建,來實現項目核心功能的穩定運行,這個時候,為了我們的數據的完整性,原子操作是必不可少的,因此學習原子操作,了解底層就顯得有必要了。