博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
顺序和屏障 收藏
阅读量:4051 次
发布时间:2019-05-25

本文共 2856 字,大约阅读时间需要 9 分钟。

   当处理多处理器之间或硬件设备之间的同步问题时,有时需要在程序代码中以指定的顺序发出读内存(读入)和写内存(存储)指令。在和硬件交互时,时常需要确保一个给定的读操作发生在其他读或写操作之前。另外,在多处理上,可能需要按写数据时的顺序读数据(通常确保后来以同样的顺序进行读取)。但是编译器和处理器为了提高效率,可能对读和写重新排序。   

所有可能重新排序和写的处理器提供了及其指令来确保顺序要求。同样也可以指示编译器不要对给定的点周围的指令进行重新排序,这些确保顺序的指令称为屏障(barrier)。

编译器会在编译时按代码的顺序编译,这种顺序是静态的。但是处理器会重新动态排序,因为处理器在执行指令期间,会在取值和分派时,把表面上看似无关的指令按自认为最好的顺序排列。这种重排序的发生是因为现代处理器为了优化其传送管道,打乱了分派和提交指令的顺序。

  不管是编译器还是处理器都不知道其他上下文中的相关代码。偶然情况下,有必要让写操作被其他代码识别,也让我们所期望的指定顺序之外的代码识别。这种情况常常发生在硬件设备上,但那是在多处理器机器上也很常见。

 

rmb()方法提供了一个“读”内存屏障,它确保跨越rmb()的载入动作不会发生重排序。在rmb()之前的载入操作不会被重新排在该调用之后;在rmb()之后的载入操作不会被重新排列在该调用之前。

  1. 在<System.h(include\asm-i386)>中
  2. #define rmb() alternative("lock; addl $0,0(%%esp)", "lfence", X86_FEATURE_XMM2) 

 

  1. 在<Alternative.h(include\asm-i386)>中
  2. #define alternative(oldinstr, newinstr, feature)    \
  3.     asm volatile ("661:\n\t" oldinstr "\n662:\n"             \
  4.               ".section .altinstructions,\"a\"\n"            \
  5.                .align 8\n"                       \
  6.                .quad 661b\n"                      \
  7.                .quad 663f\n"         \
  8.                .byte �\n"                 \
  9.                .byte 662b-661b\n"             \
  10.                .byte 664f-663f\n"        \
  11.               ".previous\n"                 \
  12.               ".section .altinstr_replacement,\"ax\"\n"     \
  13.               "663:\n\t" newinstr "\n664:\n"    \
  14.               ".previous" :: "i" (feature) "memory")

wmb()方法提供了一个“写”内存屏障。该函数与rmb()类型,区别仅仅是它是针对存储而非载入。它确保跨越屏障的存储不发生重新排序。如果一个体系结构不执行打乱存储(比如Intel x86芯片就不会),那么wmb()就什么也补做。

 

     

    1. #ifdef CONFIG_X86_OOSTORE
    2. #define wmb() alternative("lock; addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM)
    3. #else
    4. #define wmb()   __asm__ __volatile__ ("": :"memory")
    5. #endif

    rmb()和wmb()方法相当于指令,它们告诉处理器在继续执行前提交所有尚未处理的载入或存储指令。

     

    mb()方法既提供了读屏障也提供了写屏障。载入和存储动作都不会跨越屏障重新排序。这是因为一条单独的指令(通常和rmb()使用同一个指令)既可以提供载入屏障,也可以提供存储屏障。

     

    1. #define mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2)

    read_barrier_depends()是rmb()的变种,它提供一个读屏障,但是仅仅是针对后续读操作锁依赖的那些载入。因为屏障后的读操作依赖于屏障前的读操作,因此,该屏障确保屏障前的读操作在屏障后的读操作之前完成。基本上说,该函数设置一个读屏障,如rmb(),但是只真对特定的读---也就是那些相互依赖的读操作。

     

    1. #define read_barrier_depends()  do while(0)

    第2个<programlisting>主要是为了说明read_barrier_depends()用于数据依赖的读操作,由于y和x不是数据依赖的,因为没有成功设置读屏障,导致x=a在y=b之前运行,于是a可能还是0的时候就被赋给x了。此时,对于没有数据依赖的读操作应该使用rmb()来提供读屏障。

    宏smp_rmb()、smp_wbm()、smp_mb()和smp_read_barrier_depends()提供了一个有用的优化。在SMP 内核中,它们被定义成常用的内存屏障,而在单处理器内核中,它们被定义成编译器的屏障

     

    1. #ifdef CONFIG_SMP    //在SMP 内核中,它们被定义成常用的内存屏障
    2. #define smp_mb()    mb()
    3. #define smp_rmb()   rmb()
    4. #define smp_wmb()   wmb()
    5. #define smp_read_barrier_depends()  read_barrier_depends()
    6. #define set_mb(var, value) do (void) xchg(&var, value); while (0)
    7. #else    //在单处理器内核中,它们被定义成编译器的屏障
    8. #define smp_mb()    barrier()
    9. #define smp_rmb()   barrier()
    10. #define smp_wmb()   barrier()
    11. #define smp_read_barrier_depends()  do while(0)
    12. #define set_mb(var, value) do var value; barrier(); while (0)
    13. #endif

    barrier()方法可以防止编译器跨越屏障对载入或存储操作进行优化。编译器不会重新组织存储或载入操作而防止改变C代码的效果和现有数据的依赖关系。但是,它不知道当前上下文之外会发生什么事。前面讨论的内存屏障可以完成编译器屏障的功能,但是后者比前者轻量得多。实际上,编译器屏障机会是空闲的,因为它只是防止编译器可能重排指令。

    1. 在<Compiler.h(include\linux)>中
    2. #ifndef barrier
    3. define barrier() __memory_barrier()
    4. #endif

     

    为最坏的情况(即排序能力最弱的处理器)使用恰当的内存屏蔽,这样代码才能在编译时执行针对体系结构的优化。

    转载地址:http://hqsci.baihongyu.com/

    你可能感兴趣的文章
    pidgin-lwqq 安装
    查看>>
    mint/ubuntu安装搜狗输入法
    查看>>
    C++动态申请数组和参数传递问题
    查看>>
    opencv学习——在MFC中读取和显示图像
    查看>>
    C/C++中关于动态生成一维数组和二维数组的学习
    查看>>
    JVM并发机制探讨—内存模型、内存可见性和指令重排序
    查看>>
    nginx+tomcat+memcached (msm)实现 session同步复制
    查看>>
    c++模板与泛型编程
    查看>>
    WAV文件解析
    查看>>
    WPF中PATH使用AI导出SVG的方法
    查看>>
    WPF UI&控件免费开源库
    查看>>
    QT打开项目提示no valid settings file could be found
    查看>>
    Win10+VS+ESP32环境搭建
    查看>>
    android 代码实现圆角
    查看>>
    flutter-解析json
    查看>>
    android中shader的使用
    查看>>
    java LinkedList与ArrayList迭代器遍历和for遍历对比
    查看>>
    drat中构造方法
    查看>>
    JavaScript的一些基础-数据类型
    查看>>
    转载一个webview开车指南以及实际项目中的使用
    查看>>