使用xchg的自旋锁

使用xchg的自旋锁,c,x86,locking,C,X86,Locking,我试图在C中使用内嵌汇编和命令xchg实现最简单的自旋锁(使用TAS)。由于我的编译器错误消息越来越离奇,我开始变得头发花白,所以我决定在这里提问。我也很抱歉,如果这个问题已经得到了回答,因为我没有找到任何东西 关于我在这个主题上的编程经验,可能需要说些什么。我用C做得很好(在我看来,考虑到标准问题)。此外,我相信我了解x86的基本知识,但当涉及到内联汇编程序中的约束时,我完全不知所措。我发现做谷歌搜索更让我困惑,因为很多消息来源说的都是完全不同的东西 到目前为止,我的代码是: int acqu

我试图在C中使用内嵌汇编和命令xchg实现最简单的自旋锁(使用TAS)。由于我的编译器错误消息越来越离奇,我开始变得头发花白,所以我决定在这里提问。我也很抱歉,如果这个问题已经得到了回答,因为我没有找到任何东西

关于我在这个主题上的编程经验,可能需要说些什么。我用C做得很好(在我看来,考虑到标准问题)。此外,我相信我了解x86的基本知识,但当涉及到内联汇编程序中的约束时,我完全不知所措。我发现做谷歌搜索更让我困惑,因为很多消息来源说的都是完全不同的东西

到目前为止,我的代码是:

int acquire_lock(int* lock){
    int val = 1;
    int lock_cont;
    while((lock_cont = *lock) != 0){
            __asm__("xchg %0 %1" : "+q" (val), "+m" (lock_cont));
    }
    return 0;
}
这不起作用的原因可能很明显,但却让我发疯。我也尝试过一些其他的变体,但没有一个是编译的。现在你可能已经知道了,我真的不知道自己在做什么,所以我非常乐意接受任何建议

以下是我的编译器消息,以防有帮助:

my_lock.c:17:11: error: unexpected token in argument list
            __asm__("xchg %0 %1" : "+q" (val), "+m" (lock_cont));
                    ^
<inline asm>:1:12: note: instantiated into assembly here
    xchg %eax -16(%rbp)
              ^
1 error generated.
my_lock.c:17:11:错误:参数列表中出现意外标记
__asm_uuuuhg%0%1:“+q”(val),“+m”(lock_cont));
^
:1:12:注意:此处实例化为程序集
xchg%eax-16(%rbp)
^
生成1个错误。
提前谢谢

绝望的学生

编辑:

我把锁打开了。。一个do-while循环和逗号就成功了。现在我遇到了一个新问题,我的锁实现似乎仍然不能保证独占访问。。我将发布整个代码,如果有任何建议/批评,我将非常高兴

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

//shared variable
int x;

//new lock instance that's consistent over function calls
int* new_lock(){
        int* ptr = (int*)(malloc(sizeof(int)));
        *ptr = 0;
        return ptr;
}

//set content of lock atomically to 1
int acquire_lock(int* lock){
        int val = 1;
        do{
                __asm__("xchg %0, %1" : "+q" (val), "+m" (*lock));
        }while(val - (*lock) == 0);
        return 0;
}

//release the lock
int release_lock(int* lock){
        *lock = 0;
        return 0;
}

//free lock
int delete_lock(int* ptr){
        free(ptr);
        return 0;
}

//thread counts to 10^6
void* thread_do(void* arg){
        int* lock = (int*) arg;
        for(int i = 0; i < 100000; i++){
                acquire_lock(lock);
                x++;
                release_lock(lock);
        }
        return NULL;
}

int main(int argc, char** argv){
        pthread_t thread0, thread1;
        int* lock = new_lock();
        pthread_create(&thread0, NULL, thread_do, lock);
        pthread_create(&thread1, NULL, thread_do, lock);
        pthread_join(thread0, NULL);
        pthread_join(thread1, NULL);
        printf("%d\n",x);
        return 0;
}
#包括
#包括
#包括
//共享变量
int x;
//在函数调用上保持一致的新锁实例
int*new_lock(){
int*ptr=(int*)(malloc(sizeof(int));
*ptr=0;
返回ptr;
}
//将锁的内容原子设置为1
int获取锁定(int*lock){
int-val=1;
做{
__asm_uuhg%0,%1:“+q”(val),“+m”(*lock));
}而(val-(*lock)==0);
返回0;
}
//松开锁
内部释放锁定(内部*锁定){
*lock=0;
返回0;
}
//自由锁
int delete_lock(int*ptr){
免费(ptr);
返回0;
}
//线程计数为10^6
void*thread\u do(void*arg){
int*lock=(int*)arg;
对于(int i=0;i<100000;i++){
获取_锁(lock);
x++;
释放锁(锁);
}
返回NULL;
}
int main(int argc,字符**argv){
pthread_t thread0,thread1;
int*lock=new_lock();
pthread_create(&thread0,NULL,thread_do,lock);
pthread_create(&thread1,NULL,thread_do,lock);
pthread_join(thread0,NULL);
pthread_join(thread1,NULL);
printf(“%d\n”,x);
返回0;
}
编辑2:


我的锁实际上起作用了,正如在thread_do函数中锁定整个循环时所看到的那样。对这个结果不太满意,因为这个锁x很长时间了,但我想我不得不接受这个。。我假设问题是,在我的asm指令和来自的比较之间,当锁定和解锁是如此快速的指令流(对于线程中的循环)时,我不能保证原子性,因为我没有看到C中的解决方法(欢迎建议),我将坚持使用这种实现,因为总体思路似乎是正确的。

如果您试图创建自旋锁,可能需要使用强大的原子比较交换

下面是一个使用GCC内置项获取锁定的简单实现:

int acquire_lock(int* lock) 
{
    while (__sync_val_compare_and_swap (lock, 0, 1) != 0)
    {
        // Do something while waiting for the lock ?
    }
    return 0;
}
与内联ASM相比,编译器内置具有可读性更强、可移植性更强的优点


关于代码中的错误,操作数之间缺少逗号。 这句话:

__asm__("xchg %0 %1" : "+q" (val), "+m" (lock_cont));
应该是:

__asm__("xchg %0, %1" : "+q" (val), "+m" (lock_cont));

多谢各位!但是,由于这是一个操作系统课程的练习,我们有可能使用gcc内置和/或内联程序集,以获得更多的程序集,因此我决定继续使用可读性较差的程序集。缺少逗号是一个非常令人尴尬的错误。。问题在于while循环是按原样给出的,没有解释asm部分(或任何内联asm),但我想得越多,这就越不管用。我会再考虑一下,如果我提出了一个使用xchg的有效解决方案,我会发布它,以防其他人会遇到这个问题。@如果你想使用xchg实现ASM,也许你会想看看。这和你使用的算法一样。再次感谢!维基百科说了我已经怀疑过的。。也就是说,我需要一个do-while循环,而不是while循环。我的锁现在可以用了,但仍然不能保证独占访问。。但既然它能工作,我将尝试找出剩下的问题是什么。对于自旋锁,我认为最好是1。检查锁是否自由。2.尝试使用
xchg
来存储1并查看旧值。3.如果
xchg
加载了1,则另一个线程在1和2之间获取了锁:因此转到1(并以只读方式旋转检查锁,而不是锁cmpxchg)。您永远不希望在
lock
ed总线循环中旋转:这会使等待锁的每个线程都减慢系统的速度。例如,请参阅对另一个问题的回答。使用纯C尽可能多地实现这一点(例如,对于解锁功能中的释放存储,使用C11
stdatomic
)会很好。这是您希望编译器发出的asm的一个示例。