ARM指令集—SWP指令
SWP和SWPB是ARM指令集中對存儲單元的原子操作。即對存儲單元的一次讀和一次不可被切割。
SWP和SWPB分別完畢存儲器和寄存器之間 一個字(32bit)和一個字節(8bit)的數據交換。
SWP指令主要是完畢ARM體系架構處理器的同步操作。在Linux操作系統中實現信號量的操作。可是此指令在ARMv6架構后就沒有採用了,而是通過擴展的LDREX和STREX實現。本片文章主要介紹SWP的功能,對於LDREX和STREX以后再介紹。
SWP的指令格式例如以下:
SWP {}{B} Rd, Rm, [Rn]
當中Rd是目的寄存器,從存儲器中讀到的值存放於此寄存器中
Rm寄存器是操作數。會將此寄存器中的值存放於存儲單元中
[Rn]是寄存器間接尋址,Rn保存的是某個存儲單元的地址
如果[Rn]中存放的是信號量。當某程序要改動信號量時,則會調用SWP指令完畢對信號量的操作,即對這個存儲單元的讀和寫是一個原子操作。不會被打斷,命令的運行步驟例如以下圖1所看到的:
圖1
當多個程序要訪問他們共享的資源時,我們必需要做好同步機制以保證數據的安全。通常,共享的資源能夠是一段共享內存或者是外部設備。訪問這些資源的能夠使CPU、進程或者是線程。
為了完畢同步機制,會採用一個原子變量來保存資源的狀態。
比例如以下圖2所看到的,用一個二元信號量(0或者1)來實現共享資源的同步,當進程A 和 進程B都要訪問信號量Semaphore。
圖2
對於A進程。先訪問到信號量Semaphore發現狀態可用,應該立即會改動Semaphore的狀態。告訴其它進程此資源正在被使用。可是可能因為時間片恰好用完,系統調度到進程B。
進程B訪問到信號量時發現狀態也可用。於是
改動Semaphore告訴其它進程此資源正在被使用,等到系統再次調度到進程A時,進程A卻不知道進程B已改動了Semaphore而且使用了公共資源,於是接着上次未完畢的任務,
開始改動Semaphore而且開始使用公共資源。因此。遇到這種情況的話,信號量形同虛設並沒有起到同步的作用。
所以假設使用SWP指令,通過上面的介紹,SWP指令時完畢對存儲單元的一次讀和寫的原子操作。就能夠避免這種情況。
以下的匯編代碼是通過SWP實現相互排斥的樣例
EXPORT lock_mutex_swp
lock_mutex_swp PROC
LDR r2, =locked
SWP r1, r2, [r0] ; Swap R2 with location [R0], [R0] value placed in R1
CMP r1, r2 ; Check if memory value was ‘locked’
BEQ lock_mutex_swp ; If so, retry immediately
BX lr ; If not, lock successful, return
ENDP
EXPORT unlock_mutex_swp
unlock_mutex_swp
LDR r1, =unlocked
STR r1, [r0] ; Write value ‘unlocked’ to location [R0]
BX lr
ENDP
當然,除了上面的情況,還可能因為中斷的產生導致讀和寫的操作被打斷。
在一些任務比較簡單的系統中,能夠在關鍵的代碼中利用禁止中斷的方式來保證對數據操作的原子性,然而對於如今復雜的多任務操作系統,禁止中斷的做法顯然不是有效的解決方法。
所以SWP通過特殊的訪問方式,不須要禁止中斷。可是這樣也會延長中斷的響應時間。隨着處理器的高速發展,多核處理器已經顯示出了強大的優勢,同步的問題顯得更加明顯。如圖3所看到的,一個系統由一個Cortex-A8和Cortex-M4組成。他們都會訪問一同一段存儲空間。
圖3
SWP指令在這樣的模式下。就顯得非常尷尬了,假設依舊採用原來的特殊訪問模式,可能會大大減少多核處理的性能。
所以從ARMv6架構以后。不再使用SWP指令實現同步的功能,而是添加了LDREX和STREX指令完畢相關的操作。
具體使用情況,會在LDREX和STREX的文章中具體說明。