没有得到使用CMPXCHG8B为无符号长预期的输出 [英] Not getting expected output using cmpxchg8b for unsigned long

查看:119
本文介绍了没有得到使用CMPXCHG8B为无符号长预期的输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图写一个简单的比较和交换内联汇编code。这里是我的code

I am trying to write a simple compare and swap inline assembly code. Here is my code

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
static inline unsigned long
cas(volatile unsigned long* ptr, unsigned long old, unsigned long _new)
{
    unsigned long prev=0;
    asm volatile("lock cmpxchg8b %0;"
                 : "=m"(prev)
                 : "m"(*ptr),"a"(old),"c"(_new)
                 );
    return prev;
}

int main()
{

    unsigned long *a;
    unsigned long b=5,c;
    a=&b;
        c=cas(a,b,6);
    printf("%lu\n",c);
    return 0;
}

这code最好应打印5,但它是印刷0有什么不对我的code?请帮助。

This code should ideally print 5 but it is printing 0. What is wrong in my code ?Please help.

推荐答案

让我先说启动使用内联汇编是一个坏主意。让我再说一遍,使用内联汇编是一个坏主意。你可以写,为什么使用内联汇编是一个坏主意整个维基条目。请考虑使用内置函数(如gcc的 __sync_bool_compare_and_swap )或库,例如&LT;原子&GT;来代替。

Let me start by saying "Using inline asm is a bad idea." And let me repeat that "Using inline asm is a bad idea." You could write an entire wiki entry about why using inline asm is a bad idea. Please consider using builtins (like gcc's __sync_bool_compare_and_swap) or libraries like <atomic> instead.

如果你正在编写制作软件,使用内联汇编的风险几乎比任何好处肯定较大。如果你正在写的教育目的,然后阅读。

If you are writing production software, the risks from using inline asm are almost certainly greater than any benefit. If you are writing for educational purposes, then read on.

(对于为什么你不应该使用内联汇编进一步的说明,等待迈克尔还是彼得露面,并指出所有的事情不对的code,它是的真正的硬,甚至谁知道这东西的人,得到它的权利。)

(For a further illustration of why you shouldn't use inline asm, wait for Michael or Peter to show up and point out all the things wrong with this code. It's really hard, even for people who know this stuff, to get it right.)

下面是展示了如何使用 CMPXCHG8B 一些code。这是简单的,但应该足以给一个大致的了解。

Here is some code showing how to use cmpxchg8b. It is simple, but should be sufficient to give a general idea.

#include <stdio.h>

// Simple struct to break up the 8 byte value into 32bit chunks.
typedef union {
  struct {
     unsigned int lower;
     unsigned int upper;
  };
  unsigned long long int f;
} moo;

unsigned char cas(moo *ptr, moo *oldval, const moo *newval)
{
   unsigned char result;

#ifndef __GCC_ASM_FLAG_OUTPUTS__

   asm ("lock cmpxchg8b %[ptr]\n\t"
        "setz %[result]"
        : [result] "=q" (result), [ptr] "+m" (*ptr),
          "+d" (oldval->upper), "+a" (oldval->lower)
        : "c" (newval->upper), "b" (newval->lower)
        : "cc", "memory");

#else

   asm ("lock cmpxchg8b %[ptr]"
        : [result] "=@ccz" (result), [ptr] "+m" (*ptr),
          "+d" (oldval->upper), "+a" (oldval->lower)
        : "c" (newval->upper), "b" (newval->lower)
        : "memory");

#endif

   return result;
}

int main()
{
   moo oldval, newval, curval;
   unsigned char ret;

   // Will not change 'curval' since 'oldval' doesn't match.
   curval.f = -1;
   oldval.f = 0;
   newval.f = 1;

   printf("If curval(%u:%u) == oldval(%u:%u) "
          "then write newval(%u:%u)\n",
          curval.upper, curval.lower,
          oldval.upper, oldval.lower,
          newval.upper, newval.lower);

   ret = cas(&curval, &oldval, &newval);

   if (ret)
      printf("Replace succeeded: curval(%u:%u)\n",
             curval.upper, curval.lower);
   else
      printf("Replace failed because curval(%u:%u) "
             "needed to be (%u:%u) (which cas has placed in oldval).\n",
             curval.upper, curval.lower,
             oldval.upper, oldval.lower);

   printf("\n");

   // Now that 'curval' equals 'oldval', newval will get written.
   curval.lower = 1234; curval.upper = 4321;
   oldval.lower = 1234; oldval.upper = 4321;
   newval.f = 1;

   printf("If curval(%u:%u) == oldval(%u:%u) "
          "then write newval(%u:%u)\n",
          curval.upper, curval.lower,
          oldval.upper, oldval.lower,
          newval.upper, newval.lower);

   ret = cas(&curval, &oldval, &newval);

   if (ret)
      printf("Replace succeeded: curval(%u:%u)\n",
             curval.upper, curval.lower);
   else
      printf("Replace failed because curval(%u:%u) "
             "needed to be (%u:%u) (which cas has placed in oldval).\n",
             curval.upper, curval.lower,
             oldval.upper, oldval.lower);

}

的几点:


  • 如果中科院失败(因为该值不匹配),从函数的返回值是0,你的值需要的使用是OLDVAL返回。这使得试图再次简单。需要注意的是,如果你正在运行多线程(你必须不然你不会使用锁定CMPXCHG8B ),第二次尝试可能会失败不得而知为好,因为'其他线程可以再次击败你写。

  • __ __ GCC_ASM_FLAG_OUTPUTS 定义可在较新的版本的GCC(6.x的+)的。它可以让你跳过做 setz 和直接使用标志。见海合会文档了解详情。

  • If the cas fails (because the values don't match), the return value from the function is 0, and the value you need to use is returned in oldval. This makes trying again simple. Note that if you are running multi-threaded (which you must be or you wouldn't be using lock cmpxchg8b), a second attempt could conceivable fail as well, since the 'other' thread could have beaten you to the write again.
  • The __GCC_ASM_FLAG_OUTPUTS__ define is available on newer builds of gcc (6.x+). It allows you to skip doing the setz and use the flags directly. See the gcc docs for details.

至于它是如何工作的:

当我们调用 CMPXCHG8B ,我们传递一个指针到内存。这是要比较(8字节)值是在内存位置中ED​​X 8个字节:EAX。如果它们匹配,则将会写入ECX中的8个字节:EBX的内存位置和标志将被设置。如果它们不匹配,则当前值将在返回EDX:EAX和标志将被清零。

When we call cmpxchg8b, we pass it a pointer to memory. It is going to compare the (8 byte) value that is in that memory location to the 8 bytes in edx:eax. If they match, then it will write the 8 bytes in ecx:ebx to the memory location and the zero flag will be set. If they don't match, then the current value will be returned in edx:eax and the zero flag will be cleared.

所以,相比之下,与code:

So, compare that with the code:

   asm ("lock cmpxchg8b %[ptr]"

下面我们传递的指针8个字节 CMPXCHG8B

Here we are passing the pointer to the 8 bytes to cmpxchg8b.

        "setz %[result]"

在这里,我们存储由 CMPXCHG8B 设置成(结果)的标记的内容。

Here we are storing the contents of the zero flag set by cmpxchg8b into (result).

        : [result] "=q" (result), [ptr] "+m" (*ptr),

指定(结果)是输出(=),而且它必须是一个字节寄存器(Q)。此外,内存指针是IN +输出(+),因为我们将是既阅读它,写它。

Specify that (result) is an output (=), and that it must be a byte register (q). Also, the memory pointer is an in+out (+) since we will be both reading it and writing to it.

          "+d" (oldval->upper), "+a"(oldval->lower)

+符号再次表明这些值是+了。这是必要的,因为如果比较失败,EDX:EAX将由从ptr的当前值覆盖

The + signs again indicate that these values are in+out. This is necessary since if the comparison fails, edx:eax will be overwritten with the current value from ptr.

        : "c" (newval->upper), "b"(newval->lower)

这些值仅为输入。在 CMPXCHG8B 是不会改变它们的值,所以我们把它们放在第二个冒号后面。

These values are input-only. The cmpxchg8b isn't going to change their values so we put them after the second colon.

        : "cc", "memory");

由于我们正在改变的标志,我们需要通过CC通知编译器。的记忆的约束可能不是必要的,这取决于被用于正是CAS的。这可能是因为线程1是通知线程2的东西准备好处理。在这种情况下,你要绝对保证GCC不具有寄存器,它计划以后写入内存的值。它绝对要他们全部刷新到内存中的的执行 CMPXCHG8B

借助 GCC文档详细描述了扩展的asm的运作声明。如果这种解释部分还不清楚,一些阅读可能的帮助。

The gcc docs describe in detail the workings of the extended asm statement. If parts of this explanation are still unclear, some reading might help.

BTW的情况下,我忘了提,写内联汇编是一个坏主意...

BTW in case I forgot to mention, writing inline asm is a bad idea...

这篇关于没有得到使用CMPXCHG8B为无符号长预期的输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆