Linux原子操作 (Linux atomic operations)
来源:互联网 发布:如何减腓肠肌 知乎 编辑:程序博客网 时间:2024/05/01 14:19
原子操作的目的
原子操作的目的是实现对多线程共享的变量进行原子性的操作。这些操作包含读取、设置、加、减、交换(xchg)等等。例如线程A和B共享变量V,且V的初始值是1。线程A和B分别进行如下操作:
A B
V+1 V+1
当CPU实现加一操作时,有可能先从内存读取V的值,在CPU中加一,再写回内存。这样的三步操作有可能和另一个CPU的操作交织在一起。例如经过下面的操作序列,本来的目的是A,B分别对V加一,结果最终V的值变成了2而非3。由于线程调度的原因,同样的问题对于单CPU的情况一样可能出现。
AV->registerregister=1BV->registerregister=1Aregister++register=2Bregister++register=2Aregister->VV=2Bregister->VV=2原子操作与memory barrier
原子操作仅仅保证原子性,它并不能保证不同指令的执行顺序。例如对于下面的两行语句,CPU完全可以调换顺序执行。
a=1b=2
要保证指令的执行顺序,需要使用memory barrier。关于barrier的详细介绍可以参见Documentation/memory-barriers.txt。在Linux中,部分原子操作API封装了memory barrier,因此能够保证执行顺序。下面的详细介绍将描述哪些API包含了barrier。
Linux中的整数原子操作函数
由于原子操作的实现是与CPU的类型相关的,所以具体实现有所不同。下面以X86平台为例。
所有的原子操作都作用于类型为atomic_t的变量。在X86平台上它被简单的定义为一个包含一个int类型的结构体。定义成结构体的好处是防止错误的强制转换,防止绕过原子操作API去操作原子变量。
typedef struct { int counter;} atomic_t;
原子变量初始化
使用下面的宏定义初始原子变量的值。
atomic_t a = ATOMIC_INIT(i);
简单设置和读取原子变量
需要注意的是下面的API均不包含memory barrier。因此使用者需要自己在调用前后添加memory barrier。
atomic_set(&a, i);atomic_read(&a);
简单加减原子变量
根据文档下面的API均不包含memory barrier。但是X86的实现中均使用lock前缀因此具有memory barrier。
atomic_add(i, &a);atomic_sub(i, &a);atomic_inc(&a);atomic_dec(&a);Linux提供了下面的4个API用于给上面的操作提供memory barrier。不直接使用smp_mb的目的应该是减少系统开销。例如对于X86平台,由于上面的API都已经具有memory barrier,因此下面的函数都简化为了barrier()。
smp_mb__{before,after}_atomic_{dec,int}()
加减原子变量并返回更新后的值
下面的API均包含memory barrier。作为一个general rule,所有具有返回值的原子操作(除了atomic_read)都具有memory barrier。
atomic_add_return(i, &a);atomic_sub_return(i, &a);atomic_inc_return(&a);atomic_dec_return(&a);
加减原子变量并测试是否等于零
下面的API均包含memory barrier。
atomic_add_and_test(i, &a);atomic_sub_and_test(i, &a);atomic_inc_and_test(&a);atomic_dec_and_test(&a);atomic_add_negative(i, &a); // return true if the result is negative
Exchange
下面的API均包含memory barrier。
atomic_xchg(&a, new);// return the old valueatomic_cmpxchg(&a, old, new); // return the old value
Linux中的位操作原子函数
与整数原子操作函数作用于atomic_t类型不同,位操作的函数作用于无符号整数unsigned long中的某一个bit。
简单设置、清除、取反某一位
与类似的整数原子操作(atomic_add等)相同,以下API不包含memory barrier。但是X86的实现使用了lock前缀,因此具有memory barrier。
void set_bit(unsigned long nr, volatile unsigned long *addr); void clear_bit(unsigned long nr, volatile unsigned long *addr); void change_bit(unsigned long nr, volatile unsigned long *addr);
设置、清除、取反某一位并返回之前的值
与整数原子操作类似,由于具有返回值,因此以下API包含memory barrier。
int test_and_set_bit(unsigned long nr, volatile unsigned long *addr); int test_and_clear_bit(unsigned long nr, volatile unsigned long *addr); int test_and_change_bit(unsigned long nr, volatile unsigned long *addr);
读取某一位
以下API不包含memory barrier。
int test_bit(unsigned long nr, __const__ volatile unsigned long *addr);
非原子性的位操作
以上的原子位操作基本都有相对应的非原子性操作。这些操作也不具有memory barrier。当变量已经被锁保护的情况下,可以使用这些API。
void __set_bit(unsigned long nr, volatile unsigned long *addr); void __clear_bit(unsigned long nr, volatile unsigned long *addr); void __change_bit(unsigned long nr, volatile unsigned long *addr); int __test_and_set_bit(unsigned long nr, volatile unsigned long *addr); int __test_and_clear_bit(unsigned long nr, volatile unsigned long *addr); int __test_and_change_bit(unsigned long nr, volatile unsigned long *addr);
0 0
- Linux原子操作--Atomic Operations
- Linux原子操作 (Linux atomic operations)
- 没有atomic.h后如何在linux实现原子操作
- 没有atomic.h后如何在linux实现原子操作
- 原子操作(Atomic)
- 原子操作atomic
- 原子操作(atomic operation)
- atomic 原子操作练习
- sync/atomic - 原子操作
- 原子操作(atomic operation)
- CUDA atomic原子操作
- CUDA atomic原子操作
- (转)没有atomic.h后如何在linux实现原子操作
- Linux 原子操作
- Linux原子操作
- linux 原子操作
- linux 原子操作
- linux原子操作
- Android的多媒体数据库
- 开源夏令营Memcached哈希性能优化(二)
- Hibernate的检索方式
- Ubuntu下VMware无法保存配置文件问题
- unity 我的第一个项目总结(进行中)
- Linux原子操作 (Linux atomic operations)
- hdu2180 时钟
- opencv支持多种流行视频格式文件的读取
- 开始
- siegen程学android应用开发重新开始篇——改变之道。
- 自己写的一个Epoll实例
- Keil MDK的FCARM报错的记录
- cocos2d-x Action 动画
- spring framework源码下载并导入eclipse