使用 xchg 自旋锁

Spin Lock using xchg

我正在尝试使用内联汇编和命令 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;
}

由于可能显而易见但让我发疯的原因,这不起作用。我还尝试了一些其他变体,但 none 甚至编译了它们。你现在可能会说我真的不知道自己在做什么,所以我很乐意提供任何建议。

这里是我的编译器消息,以防万一:

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.

提前致谢

绝望的学生

编辑:

我的锁开始工作了.. 一个 do while 循环,逗号就成功了。现在我有一个新问题,我的锁实现似乎仍然不能保证独占访问。我将 post 整个代码并且会很高兴任何 suggestions/critics.

#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;
}

编辑 2:

我的锁确实有效,正如在 thread_do 函数内锁定整个循环时所见。对这个结果不是很满意,因为它锁定了 x 很长时间,但我想我必须忍受这个。我认为问题是我的 asm 指令和 while 的比较之间我不能保证原子性当锁定和解锁是一个如此快速的指令流时(thread_do 中的循环),因为我没有在 C 中看到解决方法(欢迎提出建议),我会坚持这个实现,因为总体思路似乎是对的。

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

这是使用 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;
}

内置编译器的优点是比内联 ASM 更具可读性和可移植性。


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

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

应该是:

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