工作__asm__ __volatile ___("":::"记忆")

vnr*_*992 43 c gcc arm volatile embedded-linux

ARM架构基本上__asm__ __volatile__ ()做了什么,有什么意义"memory"

aus*_*len 66

asm volatile("" ::: "memory");
Run Code Online (Sandbox Code Playgroud)

创建编译器级别内存屏障,强制优化器不会跨屏障重新排序内存访问.

例如,如果您需要按特定顺序访问某个地址(可能是因为该内存区域实际上是由不同的设备而不是内存支持),您需要能够告诉编译器,否则它可能只是优化您的步骤效率的缘故.

假设在这种情况下,您必须增加地址中的值,读取内容并增加相邻地址中的另一个值.

int c(int *d, int *e) {
        int r;
        d[0] += 1;
        r = e[0];
        d[1] += 1;
        return r;
}
Run Code Online (Sandbox Code Playgroud)

问题是编译器(gcc在这种情况下)可以重新安排你的内存访问,以获得更好的性能,如果你要求它(-O).可能会导致如下所示的一系列指令:

00000000 <c>:
   0:   4603        mov r3, r0
   2:   c805        ldmia   r0, {r0, r2}
   4:   3001        adds    r0, #1
   6:   3201        adds    r2, #1
   8:   6018        str r0, [r3, #0]
   a:   6808        ldr r0, [r1, #0]
   c:   605a        str r2, [r3, #4]
   e:   4770        bx  lr
Run Code Online (Sandbox Code Playgroud)

上面的值d[0]和,d[1]同时加载.让我们假设这是你想要避免的事情,然后你需要告诉编译器不要重新排序内存访问,那就是使用asm volatile("" ::: "memory").

int c(int *d, int *e) {
        int r;
        d[0] += 1;
        r = e[0];
        asm volatile("" ::: "memory");
        d[1] += 1;
        return r;
}
Run Code Online (Sandbox Code Playgroud)

所以你会得到你想要的指令序列:

00000000 <c>:
   0:   6802        ldr r2, [r0, #0]
   2:   4603        mov r3, r0
   4:   3201        adds    r2, #1
   6:   6002        str r2, [r0, #0]
   8:   6808        ldr r0, [r1, #0]
   a:   685a        ldr r2, [r3, #4]
   c:   3201        adds    r2, #1
   e:   605a        str r2, [r3, #4]
  10:   4770        bx  lr
  12:   bf00        nop
Run Code Online (Sandbox Code Playgroud)

应该注意的是,这只是编译时内存屏障,以避免编译器重新排序内存访问,因为它没有额外的硬件级指令来刷新内存或等待加载或存储完成.如果它们具有架构功能和存储器地址上的CPU仍然可以重新排序的存储器存取normal类型,而不是strongly ordereddevice(REF).

  • 在这种特殊情况下,通过声明如下参数可以在标准C中实现相同的效果:`int c(volatile int*d,volatile int*e)` (3认同)

uni*_*urf 19

此序列是编译器内存访问调度障碍,如Udo引用的文章中所述.这个是GCC特定的 - 其他编译器有其他方式来描述它们,其中一些具有更明确(和更少深奥)的语句.

__asm__ 是允许嵌套在C代码中的汇编语言语句的gcc扩展 - 这里使用它的属性是能够指定阻止编译器执行某些类型的优化的副作用(在这种情况下可能最终生成错误)码).

__volatile__需要确保asm语句本身不与任何其他易失性访问重新排序(C语言中的保证).

memory 是GCC的指令(有点)说内联asm序列对全局内存有副作用,因此不仅需要考虑对局部变量的影响.

  • +1 这个答案加上 **Udo** 是正确的。这只是*编译器*的内存屏障。它不适用于 SMP 硬件。 (2认同)
  • @Soundararajan:这个问题没有简短的答案.我建议阅读Paul McKenney关于内存访问订购要求的优秀论文:http://www.rdrop.com/~paulmck/scalability/paper/whymb.2009.04.05a.pdf以及内存障碍的Linux内核概述:https:/ /git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/memory-barriers.txt (2认同)

Udo*_*ein 9

这里的含义如下:

http://en.wikipedia.org/wiki/Memory_ordering

基本上它意味着汇编代码将在您期望的位置执行.它告诉编译器不要重新排序它周围的指令.这是在之前执行这段代码之前编码的代码,之后执行的代码将在之后执行.