如何在C中使用'cmpxchg8b'汇编指令实现原子比较和交换?

总结

我正在尝试实现无锁链表。为此,我需要使用C中的内联汇编实现原子比较和交换指令。我确实知道我需要将cmpxchg8b指令用于x86,但是我无法做到这一点。

我的节点结构如下:

typedef struct node
{
    int data;
    struct node * next;
    struct node * backlink;
}node_lf;

struct node * next指针也保持在过去的2位附加信息(标志和标志位)

我需要实现的比较和交换是这样的:

node_lf *cs (cs_arg * address, cs_arg *old_val, cs_arg *new_val )
{
    node_lf *value = (address->node)->next;
    if ((address->node->next) == old_val->node)
    {
        (address->node)->next = new_val->node;
    }
    return value;
}

cs_arg结构如下:

typedef struct csArg
{
    node_lf * node;
}cs_arg;

我的实现:

static inline node_lf* cs(cs_arg * address, cs_arg *old_val, cs_arg *new_val)
{
    node_lf * value;
    __asm__ __volatile__("lock cmpxchg8b %0; "
                :"=q"(value)
                :"a"(address->node->next), "b"(old_val->node), "c"(new_val->node)
                :"memory");
    return value;
}

这给出了一条错误消息: ../list.c:86: Error: operand type mismatch for 'cmpxchg8b' make: *** [list.o] Error 1

请帮我解决这个问题。

大卫·沃尔弗德(David Wohlferd)

让我们一次迈出这一步。首先,让我们尝试在一个简单的情况下使用__sync_val_compare_and_swap:

#include <stdio.h>

typedef struct node
 {
    int data;
    struct node * next;
    struct node * backlink;
 }node_lf;

int cs1(node_lf *address, int old_val, int new_val)
{
    int *p2 = &address->data;
    int p3 = __sync_val_compare_and_swap(p2, old_val, new_val);  

    return p3;
}

int main()
{
   node_lf n;

   n.data = 17;

   int res = cs1(&n, 1, 2);
   printf("Old Value: %d  Cur Value: %d\n", res, n.data);

   res = cs1(&n, 17, 2);
   printf("Old Value: %d  Cur Value: %d\n", res, n.data);
}

因此,这里的代码非常简单。我们正在处理node.data。我们首先将其初始化为17。然后尝试执行cas将其更改为2。但是,在第一个调用中,我们为oldval给出了不正确的值(1 vs 17)。结果,__ sync_val_compare_and_swap(正确!)不会执行交换,但会返回当前值。第二个调用确实给出了正确的旧值,但确实执行了交换并返回了旧值。

因此,运行时得到:

Old Value: 17  Cur Value: 17 <- Didn't change the value
Old Value: 17  Cur Value: 2  <- Did change the value

很简单,但并不完全是您想要的。让我们更进一步:

#include <stdio.h>

typedef struct node
 {
    int data;
    struct node * next;
    struct node * backlink;
 }node_lf;

typedef struct csArg
{
    node_lf * node;
}cs_arg;

node_lf *cs2(node_lf *address, const cs_arg *old_val, const cs_arg *new_val)
{
    unsigned long long *p2 = (unsigned long long *)&address->next;
    unsigned long long p3 = __sync_val_compare_and_swap (p2, (unsigned long long)old_val->node, (unsigned long long)new_val->node);  

    return (node *)p3;
}

int main()
{
   node_lf n;
   cs_arg oldval, newval;

   n.next = (node *)18;
   oldval.node = (node *)1;
   newval.node = (node *)2;

   node *res2 = cs2(&n, &oldval, &newval);
   printf("Old Value: %p  Cur Value: %p\n", res2, n.next);

   oldval.node = (node *)18;
   res2 = cs2(&n, &oldval, &newval);
   printf("Old Value: %p  Cur Value: %p\n", res2, n.next);
}

在这种情况下,我们尝试通过使用2个cs_args中的节点来更新node.next。这里主要要注意的是,由于__sync_val_compare_and_swap适用于整数类型,因此我们需要将指针转换为适当大小的int。这里的假设是我们使用的是64位编译器,因此指针为8个字节(与unsigned long long相同)。

此处实际上并不需要使用cs_arg结构。使用node *应该可以正常工作。但是您使用了它,因此大概这里有一些长期计划。

运行它,我们看到:

Old Value: 0000000000000012  Cur Value: 0000000000000012  <- Didn't change the value
Old Value: 0000000000000012  Cur Value: 0000000000000002  <- Did change the value

请注意,如果您运行的是x64,则可以将其最多增加16个字节(使用__int128和cmpxchg16b)。但是,有一些技巧需要注意。例如,数据必须是16字节对齐的(node.next不是NOT)。

现在,完成所有这些操作之后,您对“需要将address-> node-> next的旧值作为返回值”的要求似乎很可疑。查看上面的代码,如何判断cas是否失败?如果有效,则返回18,如果失败,则返回18。由于这就是您所说的那样,所以我尝试提供该功能。

但是我认为您确实想知道它是否有效(可能是为了再试一次)。这样的话,也许更像这样:

#include <stdio.h>

typedef struct node
 {
    int data;
    struct node * next;
    struct node * backlink;
 }node_lf;

bool cs2(node_lf *address, const node *old_val, const node *new_val)
{
    unsigned long long *p2 = (unsigned long long *)&address->next;
    return __sync_bool_compare_and_swap (p2, (unsigned long long)old_val, (unsigned long long)new_val);
}

int main()
{
   node_lf n;
   node *oldval, *newval;

   n.next = (node *)18;
   oldval = (node *)1;
   newval = (node *)2;

   bool res2;

   do {

      printf("Trying to change %p from %p to %p: ", n.next, oldval, newval);
      res2 = cs2(&n, oldval, newval);
      printf("Worked: %d  Cur Value: %p\n", res2, n.next);
      if (res2)
         break;

      oldval = n.next;
   } while (1);
}

当您退出循环时,oldval将是之前的内容(必须存在或cas会失败),newval将是实际写入的内容。请注意,如果这确实是多线程的,则不能保证newval与n.next相同,因为另一个线程可能已经出现并再次对其进行了更改。

尽管使用汇编程序可以为您节省一条或两条指令,但是在可读性,可维护性,可移植性等方面的优势几乎可以肯定是值得的。

除非这是班级项目,否则老师需要嵌入式asm。在这种情况下,请查看https://stackoverflow.com/a/37825052/2189500

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

使用 Nasm 汇编程序的 CMPXCHG8B 示例

来自分类Dev

了解cmpxchg8b / cmpxchg16b操作

来自分类Dev

C ++原子CAS(比较和交换)操作不会更改值

来自分类Dev

如何在C中使用原子变量?

来自分类Dev

如何在Java中使用CAS(比较和交换)?

来自分类Dev

如何在JPA和Hibernate中使用Java 8 LocalDateTime

来自分类常见问题

如何在C ++中使用原子防止关键部分访问

来自分类Dev

如何在C ++中使用原子防止关键部分访问

来自分类Dev

如何在Linux C内联汇编中使用宏

来自分类Dev

如何在汇编代码中使用c变量

来自分类Dev

如何在C中使用64位内联汇编?

来自分类Dev

如何在Linux C内联汇编中使用宏

来自分类Dev

如何在GNU汇编器中使用ins指令

来自分类Dev

如何在GNU汇编器中使用ins指令

来自分类Dev

如何在C ++模板中使用比较表达式?

来自分类Dev

如何在 C# 中使用 opencl 实现矩阵乘法

来自分类Dev

如何在ANSI C中使用strsignal和WCOREDUMP?

来自分类Dev

如何在C中使用简单的文件输入和输出

来自分类Dev

如何在C#和MFCC提取中使用OpenSmile

来自分类Dev

如何在 C 中使用指针和结构

来自分类Dev

如何在Bootstrap-Vue的b-col和b-row中使用v-for?

来自分类Dev

如何在C ++中使用支持比较器的模板实现容器?

来自分类Dev

C指令的汇编语言实现

来自分类Dev

如何在C#中使用LDAP以编程方式启用AD用户的交换和Lync帐户

来自分类Dev

如何在C#中使用LDAP以编程方式启用AD用户的交换和Lync帐户

来自分类Dev

如何在Angular 8的抽象类和抽象类的实现中使用@Component装饰器?

来自分类Dev

我可以使用cmpxchg16b原子复制一个指针到一个指针和一个int,同时增加int(原子引用计数)吗?

来自分类Dev

如何在C ++中使用ofstream将512x512像素阵列写入bmp文件(256色和8 bpp)?

来自分类Dev

如何在C ++中使用文件流将正确无符号的__int8数组读取和写入二进制文件

Related 相关文章

  1. 1

    使用 Nasm 汇编程序的 CMPXCHG8B 示例

  2. 2

    了解cmpxchg8b / cmpxchg16b操作

  3. 3

    C ++原子CAS(比较和交换)操作不会更改值

  4. 4

    如何在C中使用原子变量?

  5. 5

    如何在Java中使用CAS(比较和交换)?

  6. 6

    如何在JPA和Hibernate中使用Java 8 LocalDateTime

  7. 7

    如何在C ++中使用原子防止关键部分访问

  8. 8

    如何在C ++中使用原子防止关键部分访问

  9. 9

    如何在Linux C内联汇编中使用宏

  10. 10

    如何在汇编代码中使用c变量

  11. 11

    如何在C中使用64位内联汇编?

  12. 12

    如何在Linux C内联汇编中使用宏

  13. 13

    如何在GNU汇编器中使用ins指令

  14. 14

    如何在GNU汇编器中使用ins指令

  15. 15

    如何在C ++模板中使用比较表达式?

  16. 16

    如何在 C# 中使用 opencl 实现矩阵乘法

  17. 17

    如何在ANSI C中使用strsignal和WCOREDUMP?

  18. 18

    如何在C中使用简单的文件输入和输出

  19. 19

    如何在C#和MFCC提取中使用OpenSmile

  20. 20

    如何在 C 中使用指针和结构

  21. 21

    如何在Bootstrap-Vue的b-col和b-row中使用v-for?

  22. 22

    如何在C ++中使用支持比较器的模板实现容器?

  23. 23

    C指令的汇编语言实现

  24. 24

    如何在C#中使用LDAP以编程方式启用AD用户的交换和Lync帐户

  25. 25

    如何在C#中使用LDAP以编程方式启用AD用户的交换和Lync帐户

  26. 26

    如何在Angular 8的抽象类和抽象类的实现中使用@Component装饰器?

  27. 27

    我可以使用cmpxchg16b原子复制一个指针到一个指针和一个int,同时增加int(原子引用计数)吗?

  28. 28

    如何在C ++中使用ofstream将512x512像素阵列写入bmp文件(256色和8 bpp)?

  29. 29

    如何在C ++中使用文件流将正确无符号的__int8数组读取和写入二进制文件

热门标签

归档