Chinaunix首页 | 论坛 | 博客
  • 博客访问: 389972
  • 博文数量: 62
  • 博客积分: 388
  • 博客等级: 一等列兵
  • 技术积分: 1032
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-03 20:18
文章分类

全部博文(62)

文章存档

2017年(5)

2016年(3)

2015年(3)

2014年(8)

2013年(15)

2012年(28)

分类: LINUX

2017-01-11 14:21:18

原子变量原理分析

内核中同步机制有很多,其中原子变量和自旋锁机制是使用的很普遍的两种机制。周末心情愉快,借这样的时间写一篇分析的文章,也不完全荒废这美好的时间。这种无聊的屌似情调也只能用“呵呵”二字表达了。

闲话少扯,直入主题。话说这原子操作为何物,直白的说就是atomic_readatomic_write。顾名思义,原子操作就是,对变量的操作是一个整体而不可以打断的。需要这样的操作的最直接原因是,在多核系统中,对内存中变量的操作往往不是一个整体。比如,对内存的写操作往往是这样的过程:

如果两个cpu同时执行同一个变量的自增操作,那么根据上面这个过程来分析,最后得到的变量只会被增加1而不是增加2,从而可以看出原子操作在多核系统上的重要性。

下面我们以atomic_add作为一个例子,来分析arm体系架构下原子加是如何实现的。


static inline int atomic_add_return(int i, atomic_t *v)

{

unsigned long tmp;

int result;


__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");


return result;

}


首先%0 代表变量result%1代表tmp%2代表(&v->counter),%3代表i。而正在实现原子操作的两条指令是ldrexstrex

ldrex %0, [%2]

这条指令为读取&v->counter地址的内容到寄存器result

add %0%0%3

这条指令为将寄存器resulti相加,将结果保存到result

strex %1%0[%2]

这条指令是将result寄存器中的值写道&v->counter地址内存的位置

bne 1b

这个指令比较关键,如果上面strex执行成功了,%1将被设置为0,则设置成功,处理结束了,如果%1被设置成为了1,表示设置失败,然后惨烈的重新跳到1:位置重新开始上面的过程。


ldrexstrexld/st操作的升级版本。ldrex从内存中读取变量的时候,对于读取地址做了标记,并记录了读取该地址的cpuid。如果在该cpu调用strex之前,有其它核调对同一个地址调用了ldrex,那么这个核在反存时执行strex发现记录的cpuid不对,就会设置操作失效。

同样,如果单个cpu中,ldrex操作被其它ldrex操作打断,同样会导致整个操作失败。原因是strex操作会将上次记录的地址和cpuid清除掉。


对于单核的系统,原子操作则很好实现,只需要简单的关闭中断,操作就不会被打断。

static inline int atomic_add_return(int i, atomic_t *v)

{

unsigned long flags;

int val;


raw_local_irq_save(flags);

val = v->counter;

v->counter = val += i;

raw_local_irq_restore(flags);


return val;

}



阅读(825) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~