美文网首页Linux 相关文章Linux内联汇编相关
CAS与内存屏障: c/c++的内联汇编(S0)

CAS与内存屏障: c/c++的内联汇编(S0)

作者: Quasars | 来源:发表于2018-07-08 16:19 被阅读2次

    c++的CAS与内存屏障: c/c++的内联汇编(S0)

    多线程编程中偶尔需要接触一些底层的东西,如CAS,原子操作,内存屏障甚至有时需要自己包裹系统调用,这边从abc开始,即内联汇编的语法,总结一下目前工作中遇到的相关东西。

    语法

    • 基本语法
    asm(
      汇编语句
      汇编语句
      ...
      [:输出参数关联列表]
      [:输入参数关联列表]
      [:破坏寄存器列表]
      )
    

    例子1:

    /// add
    int a, b;
    
    /// b = a + b
    asm(
      "movl %1,%%eax\n\t"
      "addl %0,%%eax\n\t"
      "movl %%eax,%1\n\t"
      :"+r"(b)
      :"r"(a)
      :"%eax"
      )
    
    • 关联列表语法

      • "=" : 标识该寄存器是只写的
      • "+" : 标识该寄存器既读又写
      • "r" : 由编译器分配合适的寄存器,在指令中以%0,%1,...引用. 0,1,2...以出现的顺序编号.如上面变量b关联的是0,而a关联的是1.
      • "m" : 关联时会取关联变量的地址,并且在汇编代码中对应的%n会是1个地址,并且使用时编译器会帮你加上内存寻址修饰(AT&T汇编中即为这种形式:ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER).m映射到汇编语句中起的作用类似于解引用*.
      int x, a = 10;
      asm( "movl %1,%0":"=m"(x):"r"(a):);  //编译成类似 movl %edx,-32(%rbp), `%0`被`-32(%rbp)`替换
      

      所以,如果传入内联汇编的东西是地址(如函数需要修改传入参数的值,受制于值传递,需要传入地址),想在内联汇编中修改某个地址,有2种做法:

      1. 传入地址值给寄存器并在汇编中自行解引用,以r或abc等方式关联,加上().
      2. 传入地址的解引用值,以m方式关联,汇编中对应的操作数不自行解引用.
    • 其他的注意点.asm

    1. 汇编语句中的寄存器需要2个百分号,r关联的操作数只需1个百分号.原因是该内联汇编语句是以字符串的形式被编译器消费的.同理,语句间分隔符需要显式地给\n\t也是同样的道理. 注意:不能给;,否则后面的语句在汇编中将被注释!
    2. gcc默认是at&t语法.也就是源操作数跟目的操作数位置与上学时学的masm汇编相反.
    3. AT&T解引用操作符是小括号而不是中括号.
    4. movl不允许两个操作数都为内存地址.
    • AT&T语法,常用简单汇编指令

      • movl s,d
      • addl s,d
    • 下面给出几个例子实现asm_add和asm_swap

    1   #include <iostream>
    2
    3
    4   // test for inline asm
    5   /*
    6    * asm(
    7    *  "汇编语句1;\n"
    8    *  "汇编语句2;\n"
    9    *  "汇编语句3;\n"
    10   *  :"输出列表"
    11   *  :"输入列表"
    12   *  :"破坏寄存器列表"
    13   * )
    14   *
    15   */
    16  int AsmAdd0(int a, int b) {
    17  asm(
    18    "movl %1,%%eax\n\t"- 练习
      - asm_add
      - asm_swap
    19    "addl %0,%%eax\n\t"
    20    "movl %%eax,%0\n\t"
    21    :"+r"(b)
    22    :"r"(a)
    23    :"%eax"
    24    );
    25    return b;
    26  }
    27
    28  void AsmAdd1(int a, int b, int* c) {
    29  asm (
    30    "addl %1, %2\n\t"
    31    "movl %2, (%0)\n\t"
    32    :"=r"(c)
    33    :"r"(a), "r"(b)
    34  );
    35  }
    36
    37  void AsmAdd2(int a, int b, int* c) {
    38  asm (
    39    "addl %1, %2\n\t"
    40    "movl %2, %0\n\t"
    41    :"=m"(*c)
    42    :"r"(a), "r"(b)
    43  );
    44  }
    45
    46  /// *b <----- a
    47  ///
    48  void AsmSet(int a, int *b) {
    49      asm(
    50      "movl %1, %%eax\n\t"
    51      "movl %%eax, %0\n\t"
    52      :"=m"(*b)
    53      :"r"(a)   
    54      :"%eax"
    55      );
    56  }
    57
    58
    59
    60  void testM() {
    61      int x;
    62      int a = 10;
    63      asm( "addl %1, %%ebx\n\t"
    64           "movl %1,%0\n\t"
    65           "addl %%eax, %%ebx"
    66           :"=m"(x):"r"(a):"%eax","%ebx"); //%0会被替换为`(x的偏移量)`
    67      std::cout << x << std::endl;
    68  }
    69
    70  template<typename T>
    71  void SwapByAsm(T* a, T* b) {
    72      asm(
    73        "movq (%0), %%rax\n\t"
    74        "movq (%1), %%rbx\n\t"
    75        "movq %%rbx, (%0)\n\t"
    76        "movq %%rax, (%1)\n\t"
    77        :"+r"(a), "+r"(b)
    78        :
    79        :"%rax","%rbx"
    80      );
    81  }
    82  template<>
    83  void SwapByAsm(char* a, char* b) {
    84      asm(
    85        "movb (%0), %%al\n\t"
    86        "movb (%1), %%bl\n\t"
    87        "movb %%bl, (%0)\n\t"
    88        "movb %%al, (%1)\n\t"
    89        :"+r"(a), "+r"(b)
    90        :
    91        :"%al","%bl"
    92      );
    93  }
    94
    95  int main() {
    96      int a = 20;
    97      int b = 10;
    98      int c = -1;
    99      testM();
    100     std::cout << "--------AsmAdd--------" << std::endl;
    101
    102     c = AsmAdd0(a, b);
    103     std::cout << "c:" << c << std::endl;
    104     
    105     c = -1;
    106     AsmAdd1(a, b, &c);
    107     std::cout << "c:" << c << std::endl;
    108
    109     c = -1;
    110     AsmAdd2(a, b, &c);
    111     std::cout << "c:" << c << std::endl;
    112
    113     std::cout << "-------SwapByAsm---------" << std::endl;
    114     double x0 = 0.91, x1 = 10.24;
    115     char   c0 = 'X',  c1 = 'a';
    116     
    117     std::cout << "before: x0:" << x0 << ", x1: " << x1 << std::endl;
    118     SwapByAsm(&x0, &x1);
    119     std::cout << "after : x0:" << x0 << ", x1: " << x1 << std::endl;
    120     
    121     std::cout << "before: c0:" << c0 << ", c1:" << c1 << std::endl;
    122     SwapByAsm(&c0, &c1);
    123     std::cout << "after : c0:" << c0 << ", c1:" << c1 << std::endl;
    124
    125     return 0;
    126 }
    
    • 实际用途
      • 自己封装非posix系统调用,如gittid(2)
      • 自己封装CAS汇编指令,如nginx中的CAS.
      • 内存屏障.

    相关文章

      网友评论

        本文标题:CAS与内存屏障: c/c++的内联汇编(S0)

        本文链接:https://www.haomeiwen.com/subject/agpouftx.html