x86和arm架構(gòu)原子操作的區(qū)別
由于x86是CISC指令集,允許在一條指令里進行兩次內(nèi)存操作,所以對i++,i__這些操作在單核條件下是原子,當然必須得是顯示使用addl r,%1這種,就可在一條指令里完成讀,寫操作。
而arm屬于RISC指令集,在一次指令執(zhí)行期間只能有一次內(nèi)存操作,所以像i++,i--這些需要先讀取內(nèi)存值然后賦值的操作,在arm架構(gòu)下沒法一條指令完成,所以就不滿足原子操作,這時怎樣實現(xiàn)原子操作呢:
我們通過代碼來看;
對于atomic_add
x86的實現(xiàn)很簡單:
static__inline__voidatomic_add(inti,atomic_t*v) |
單核情況下LOCK是空。
下面再看下atomic_add_and_test:
static__inline__inthal_atomic_add_and_test(inti,emcos_atomic_t*v) |
可能大家會想,這個有兩條指令了,能是原子的嗎?肯定是,為什么是呢?
大家要注意sete %1這個是條件指令,而這個條件(cflags)是進程相關(guān)的,即使當進程執(zhí)行完add1 %2,%0,這時發(fā)生中斷,切換到另外一個進程,當回來的時候cflags還是進程的,和沒切換的情形一樣,所以是原子。
而對于arm這需要更多工作:
#defineatomic_add(i,v)(void)atomic_add_return(i,v) |
上面可以看出是通過關(guān)中斷來實現(xiàn)的,為什么要關(guān)中斷來實現(xiàn)原子操作:
分析下:
arm對于i++會生成如下代碼:
1ldr r0,=i
2mov [r0],r1 //這個讀內(nèi)存操作
3inc r1 //如果在這個時候發(fā)生中斷,然后在中斷處理程序中也執(zhí)行i++操作就不是原子操作
4mov r1,[r0]//這個寫內(nèi)存操作
假設(shè)I=0。如果進程1執(zhí)行i++,執(zhí)行到3時被中斷打斷,然后中斷中也執(zhí)行了i++,當兩個i++執(zhí)行完了,i=1,而不是我們所要的2,這就是非原子操作的結(jié)果。
怎么解決,就是說2-4這段代碼要么不執(zhí)行,要么執(zhí)行完才能保證原子,這個在單核上通過關(guān)中斷就可以實現(xiàn),這也是上面關(guān)中斷的原因。
2.多核情況;
x86架構(gòu)下:
單指令也不是原子操作了,比如addl r,%1這種有兩次內(nèi)存操作的也不是原子操作,有可能在執(zhí)行下一次內(nèi)存操作的時候,另一個核心也讀取了這個內(nèi)存,也會造成兩次i++操作為1的錯誤結(jié)果。
解決方法是家LOCK標識,這個標識的作用是在一條指令執(zhí)行時,鎖住總線,其他核心沒法讀取,從而得到了原子操作。
arm架構(gòu)下:
arm只有v6系列后的才有多核,也才有專門的內(nèi)存原子操作機制就是ldrex,strex指令。
其源碼如下:
staticinlineintatomic_add_return(inti,atomic_t*v) |
評論