分享

x86和arm架构原子操作的区别 - 嵌入式 - 人月计划

 techres 2011-04-21
x86和arm架构原子操作的区别


-------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:
http://sjj0412.
------------------------------------------

x86和arm在原子操作上有些差别,下面一代码的形式来说明区别:
首先比较单核:
          由于x86是CISC指令集,允许在一条指令里进行两次内存操作,所以对i++,i__这些操作在单核条件下是原子,当然必须得是显示使用addl r,%1这种,就可在一条指令里完成读,写操作。
       而arm属于RISC指令集,在一次指令执行期间只能有一次内存操作,所以像i++,i--这些需要先读取内存值然后赋值的操作,在arm架构下没法一条指令完成,所以就不满足原子操作,这时怎样实现原子操作呢:
      我们通过代码来看;
对于atomic_add
     x86的实现很简单:
  static __inline__ void atomic_add(int i, atomic_t *v)
{
    __asm__ __volatile__(
        LOCK "addl %1,%0"
        :"=m" (v->counter)
        :"ir" (i), "m" (v->counter));
}
单核情况下LOCK是空。
下面再看下atomic_add_and_test:
static __inline__ int hal_atomic_add_and_test(int i, emcos_atomic_t *v)
{
        unsigned char c;

        __asm__ __volatile__(
                LOCK "addl %2,%0; sete %1"
                :"=m" (v->val), "=qm" (c)
                :"ir" (i), "m" (v->val) : "memory");
        return c;
}
可能大家会想,这个有两条指令了,能是原子的吗?肯定是,为什么是呢?
大家要注意sete %1这个是条件指令,而这个条件(cflags)是进程相关的,即使当进程执行完add1 %2,%0,这时发生中断,切换到另外一个进程,当回来的时候cflags还是进程的,和没切换的情形一样,所以是原子。
   而对于arm这需要更多工作:
#define atomic_add(i, v)        (void) atomic_add_return(i, v)

static inline int atomic_add_return(int i, atomic_t *v)
{
        unsigned long flags;
        int val;

        local_irq_save(flags);
        val = v->counter;
        v->counter = val += i;
        local_irq_restore(flags);

        return val;
}

上面可以看出是通过关中断来实现的,为什么要关中断来实现原子操作:
分析下:
arm对于i++会生成如下代码:     
1  ldr r0,=i
2  mov [r0],r1 //这个读内存操作
3  inc r1                           //如果在这个时候发生中断,然后在中断处理程序中也执行i++操作就不是原子操作
4  mov r1,[r0]  //这个写内存操作
假设I=0。如果进程1执行i++,执行到3时被中断打断,然后中断中也执行了i++,当两个i++执行完了,i=1,而不是我们所要的2,这就是非原子操作的结果。
怎么解决,就是说2-4这段代码要么不执行,要么执行完才能保证原子,这个在单核上通过关中断就可以实现,这也是上面关中断的原因。

2.多核情况;
      x86架构下:
      单指令也不是原子操作了,比如addl r,%1这种有两次内存操作的也不是原子操作,有可能在执行下一次内存操作的时候,另一个核心也读取了这个内存,也会造成两次i++操作为1的错误结果。
     解决方法是家LOCK标识,这个标识的作用是在一条指令执行时,锁住总线,其他核心没法读取,从而得到了原子操作。
      arm架构下:
      arm只有v6系列后的才有多核,也才有专门的内存原子操作机制就是ldrex,strex指令。
其源码如下: 
 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

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多