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

来源:互联网 发布:手机淘宝不能编辑运费 编辑:程序博客网 时间:2024/06/02 19:08

如果要在多线程中对同一个整型变量进行加减操作,我们知道可以通过加锁的方式保证线程同步,但仅对这一个变量加锁,是不是大材小用了?有没有类似于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

原创粉丝点击