使用xchg旋转锁定

时间:2015-03-10 16:06:46

标签: c x86 locking

我正在尝试使用命令xchg使用内联汇编在C中实现最简单的自旋锁(使用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.

提前致谢

绝望的学生

编辑:

我得到了锁定工作..一个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;
}

EDIT2:

当锁定整个循环在thread_do函数中时,我的锁实际上是可行的。对这个结果不太满意,因为这会锁定x很长一段时间,但我想我将不得不忍受这个...我假设问题是我的asm指令和比较之间我不能保证原子性当锁定和解锁是如此快速的指令流(对于thread_do中的循环),因为我没有看到C中的变通方法(建议是受欢迎的),我将坚持这个实现,因为一般的想法似乎是正确的。

1 个答案:

答案 0 :(得分:2)

如果您正在尝试制作自旋锁,您可能希望使用强大的原子比较交换。

以下是使用GCC内置的acquire_lock的简单实现:

int acquire_lock(int* lock) 
{
    while (__sync_val_compare_and_swap (lock, 0, 1) != 0)
    {
        // Do something while waiting for the lock ?
    }
    return 0;
}

Compiler builtins具有比内联ASM更具可读性和可移植性的优势。


关于代码中的错误,您在操作数之间缺少逗号。 那一行:

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

应该是:

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