所谓原子操作,就是该操作绝不会在执行完毕前被任何其他任务或事件打断。这种情况一般出现在一个变量会被多个线程使用。那么在其使用时需要避免中断。
具体下面原子操作的概念可以参考《linux内核设计与实现》第八章 内核同步方法。
在Linux里面提供了原子操作
typedef struct {
volatile int counter;
} atomic_t;
原子整数操作
|
描述
|
ATOMIC_INIT(int i)
|
在声明一个atomic_t变量时,将它初始化为i
|
int atomic_read(atomic_t *v)
|
原子地读取整数变量v
|
void atomic_set(atomic_t *v, int i)
|
原子地设置v值为i
|
void atomic_add(int i, atomic_t *v)
|
原子地给v加i
|
void atomic_sub(int i, atomic_t *v)
|
原子地从v减i
|
void atomic_inc(atomic_t *v)
|
原子地给v加1
|
void atomic_dec(atomic_t *v)
|
原子地给v减1
|
int atomic_sub_and_test(int i, atomic_t *v)
|
原子地从v减i,若结果等于0返回真,否则返回假
|
int atomic_add_negative(int i, atomic_t *v)
|
原子地从v加i,若结果是负数返回真,否则返回假
|
int atomic_dec_and_test(atomic_t *v)
|
原子地从v减1,若结果等于0返回真,否则返回假
|
int atomic_inc_and_test(atomic_t *v)
|
原子地从v加1,若结果等于0返回真,否则返回假
|
上边就是具体的函数,这些函数在不同体系里面是不同的实现。接下来分析arm的代码。
能分析一个函数,其他的函数也就简单了,所以就先分析atomic_set。
static inline void atomic_set(atomic_t *v, int i)
{
unsigned long tmp;
__asm__ __volatile__("@ atomic_set\n" /*__volatile__这个是告诉编译器别优化下面的代码*/
"1: ldrex %0, [%1]\n" /*将v->counter的值存入到tmp中*/
" strex %0, %2, [%1]\n" /*这条指令是如果是exclusive access,这将i值存储到v->counter , 如果不是,什么也不做*/
" teq %0, #0\n" /*判断tmp的值是不是0*/
" bne 1b" /*如果不是,这跳转到1,如果是则基本完成了atomic_set的命令*/
: "=&r" (tmp) /*%0*/
: "r" (&v->counter), "r" (i) /*%1 , %2*/
: "cc");
}
原子变量其他的函数基本相同。
而对于有些函数会有如下情况
static inline int atomic_add_return(int i, atomic_t *v)
{
unsigned long tmp;
int result;
smp_mb(); /*这里只是多了smp_mb() , 但在对于单cpu,这里对于并没有多少工作。*/
__asm__ __volatile__("@ atomic_add_return\n"
"1: ldrex %0, [%2]\n"
" add %0, %0, %3\n"
" strex %1, %0, [%2]\n"
" teq %1, #0\n"
" bne 1b"
: "=&r" (result), "=&r" (tmp)
: "r" (&v->counter), "Ir" (i)
: "cc");
smp_mb();
return result;
}
对于原子变量在代码使用问题是比较简单的。
atomic_t v; /* define v */
atomic_t v = ATOMIC_INIT(0); /* define u and initialize it to zero */
Operations are all simple:
atomic_set(&v, 4); /* v = 4 (atomically) */
atomic_add(2, &v); /* v = v + 2 = 6 (atomically) */
atomic_inc(&v); /* v = v + 1 = 7 (atomically) */
阅读(4262) | 评论(0) | 转发(0) |