首页 > 解决方案 > 实现我自己的互斥锁时如何从内联汇编中引用 C 中的指针

问题描述

作为一个练习,我正在尝试实现我自己的互斥库,以便在我即将推出的 C 程序中使用。建议我为此使用内联汇编,因此为 x86 (AT&T) 生成了以下代码:

#include "mymutex.h"

void init_my_mutex(my_mutex_t *mutex){
        *mutex = 1;
}

void lock_my_mutex(my_mutex_t *mutex){
        asm(    "movq $0, %%rax\n\t"    // temp = 0
                "movq $0, %%rbx\n\t"
                "1: xchgq (mutex), %%rax\n\t"
                "cmpq %%rax, %%rbx\n\t"
                "jz 1b\n\t":::"rax","rbx");
}

void unlock_my_mutex(my_mutex_t *mutex){
        *mutex = 1;
}

问题是我不知道*mutexasm()里面如何正确处理lock_my_mutexgcc -c mymutex.c -o mymutex.o编译得很好,但是当尝试编译我的测试程序count-primes.cgcc -pthread count-primes.c mymutex.o -o count-primes,我得到以下错误relocation R_X86_64_32S against undefined symbol 'mutex' can not be used when making a PIE object; recompile with -fPIC:我试过重新编译-fPIC(我不知道这应该有什么帮助),但我仍然得到同样的错误。

我的头文件如下所示:

#ifndef __mymutex_h
#define __mymutex_h

// Our mutex is very simple so it is either locked
// or unlocked and we don't keep any other information 
typedef long long my_mutex_t;

// Initializes a mutex to be unlocked
void init_my_mutex(my_mutex_t *mutex);

// Tries to grab a lock. The function only
// returns when the current thread holds the lock
void lock_my_mutex(my_mutex_t *mutex);

// Unlock the mutex. You don't need to check to see
// if the current thread holds the lock
void unlock_my_mutex(my_mutex_t *mutex);

#endif

count-primes.c中,我尝试像这样使用互斥锁:

my_mutex_t lock;
...

lock_my_mutex(&lock);
                        // Synchronized operation
unlock_my_mutex(&lock);

...

我怀疑这个问题与我在使用互斥锁时的地址有关,asm()并认为理解如何(以及为什么)这样做将使我能够解决这个问题。但在任何其他方面的帮助也非常感谢。

最好的,

史蒂芬。

标签: cx86synchronizationmutexinline-assembly

解决方案


要么使用内存约束,要么使用包含地址和内存破坏器的输入寄存器:

有内存限制:

void lock_my_mutex(my_mutex_t *mutex){
        uint64_t tmp;
        asm(    "mov $0, %1\n\t"
                "1: xchg %1,%0\n\t"
                "test %1, %1\n\t"
                "jz 1b\n\t": "+m(*mutex), "=&r"(tmp));
}

使用内存破坏器:

void lock_my_mutex(my_mutex_t *mutex){
        uint64_t tmp;
        asm volatile(
                "mov $0, %0\n\t"
                "1: xchg %0,(%1)\n\t"
                "test %0, %0\n\t"
                "jz 1b\n\t": "=&r"(tmp) : "r"(mutex) : "memory");
}

实际上,内存破坏器应该有任何一种方式来模拟这样一种想法,即由于与其他线程同步,其他对象的值可能会在编译器背后发生变化(受互斥锁保护的对象)。我更喜欢后一种方法,因为它并不意味着如果不再访问互斥对象,则可以删除 asm。

请注意,您可以通过以下方式进一步摆脱 mov:

void lock_my_mutex(my_mutex_t *mutex){
        uint64_t tmp;
        asm volatile(
                "1: xchg %0,(%1)\n\t"
                "test %0, %0\n\t"
                "jz 1b\n\t": "=&r"(tmp) : "r"(mutex), "0"(0) : "memory");
}

FWIW 我会称您编写的内容为自旋锁(一个非常糟糕的主意),而不是互斥锁。


推荐阅读