GCC的built-in方法之 —— 原子操作

如果要在多線程中對同一個整型變量進行加減操作,我們知道可以通過加鎖的方式保證線程同步,但僅對這一個變量加鎖,是不是大材小用了?有沒有類似於Linux內核中的atomic_inc()/atomic_dec()方法從指令的層面上實現操作變量的原子性?

答案是,有的。GCC提供了一系列內置函數,來完成對一些簡單的數據操作的同步。

* * *

type __sync_fetch_and_add (type *ptr, type value, ...)
type __sync_fetch_and_sub (type *ptr, type value, ...)
type __sync_fetch_and_or (type *ptr, type value, ...)
type __sync_fetch_and_and (type *ptr, type value, ...)
type __sync_fetch_and_xor (type *ptr, type value, ...)
type __sync_fetch_and_nand (type *ptr, type value, ...)

這些builtin函數的作用通過名字就可以看出來,相當於:

{ tmp = *ptr; *ptr op= value; return tmp; }   //'op'可以是add,sub,or, etc.
{ tmp = *ptr; *ptr = ~tmp & value; return tmp; }   // nand

這些函數的返回值爲內存中原來的值。
注1:其中類型type可以是1/2/4/8字節長度的整數類型或指針類型,下同。
注2:函數原型中的省略號可指明需要memory barrier的變量的列表,目前GCC忽略這個列表,保護所有全局可見的變量,下同。

* * *

type __sync_add_and_fetch (type *ptr, type value, ...)
type __sync_sub_and_fetch (type *ptr, type value, ...)
type __sync_or_and_fetch (type *ptr, type value, ...)
type __sync_and_and_fetch (type *ptr, type value, ...)
type __sync_xor_and_fetch (type *ptr, type value, ...)
type __sync_nand_and_fetch (type *ptr, type value, ...)

這些builtin函數相當於:

{ *ptr op= value; return *ptr; }
{ *ptr = ~*ptr & value; return *ptr; }   // nand

這些函數的返回值爲內存中的值。

這樣,我們就可以自定義一個原子的自增操作:

#define atomic_inc(x)   __sync_add_and_fetch(&(x), 1)

* * *

bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)

這兩個函數意思是,如果*ptr的值等於oldval(即ptr指向的內存中的值是舊值),則將新的值newval寫到*ptr中。
其中第一個函數的返回值爲bool類型,意思是,如果*ptr==oldval且newval寫入成功,則返回true。
第二個函數則返回操作之前的*ptr的值。

* * *

__sync_synchronize (...)

這個函數會製造一個full barrier。

* * *

type __sync_lock_test_and_set (type *ptr, type value, ...)

將*ptr的值設爲value,返回*ptr中操作之前的值。

* * *

void __sync_lock_release (type *ptr, ...)

釋放*ptr,即 *ptr = 0 。

我們可以用這些函數對多線程中簡單的數據操作做同步,而無需加鎖,節省了任務調度的開銷。如果在目標處理器上沒有實現這些(或其中某些)內置函數,則會報出warning。

實際上,GCC實現這些內置函數是爲了與 Intel Itanium Processor-specific Application Binary Interface 的7.4節的內容做兼容,也因此這些函數的名字沒有以”_builtin“開頭。這份文檔要求這些內置函數除了實現特定的同步效果外,還需要具備如下兩個重要屬性:
1)每個函數的操作都是原子性的(例如在MIPS中通過LL/SC指令實現)。
2)每個函數都會實現某種內存屏障,上述__sync_lock_test_and_set爲”acquire barrier”,__sync_lock_release爲”release barrier”,其他均爲”full barrier”(三種內存屏障的解釋在文檔中有說明,對應了內核中的rmb()/wmb()/mb())。

參考資料
[1]: http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Atomic-Builtins.html GCC atomic-builtins
[2]: https://uclibc.org/docs/psABI-ia64.pdf Intel processor-specific ABI

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章