Как ссылаться на указатель в C из встроенной сборки при реализации моего собственного мьютекса - PullRequest
0 голосов
/ 14 апреля 2019

В качестве упражнения я пытаюсь реализовать свою собственную библиотеку мьютекса, которая будет использоваться в моих будущих программах на Си. Для этого мне рекомендуется использовать встроенную сборку, поэтому для 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;
}

Проблема в том, что я не знаю, как правильно адресовать *mutex внутри asm() внутри lock_my_mutex. gcc -c mymutex.c -o mymutex.o компилируется нормально, но при попытке скомпилировать мою тестовую программу, count-primes.c, с gcc -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(), и думаю, что понимание того, как (и почему) это сделать, позволило бы мне выполнить упражнение. Но помощь в любом другом отношении также очень ценится.

Best

Штеффен.

Ответы [ 3 ]

2 голосов
/ 14 апреля 2019

Либо используйте ограничение памяти, либо входной регистр, содержащий адрес плюс клоббер памяти:

С ограничением памяти:

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 Я бы назвал то, что вы написали, спинлок (очень плохая идея), а не мьютекс.

0 голосов
/ 16 апреля 2019

Это точный фрагмент кода, который должен быть правильным.Очевидно, я забыл ключевое слово volatile ранее.

void lock_my_mutex(my_mutex_t *mutex){
        asm volatile (    
                "movq $0, %%rax\n\t"    // temp = 0                           
                "movq $0, %%rbx\n\t"                                                    
                "1: xchgq (%0), %%rax\n\t"                                              
                "cmpq %%rax, %%rbx\n\t"                                                 
                "jz 1b\n\t":"=r" (mutex):"r" (mutex):"rax","rbx"
                );                      
} 
0 голосов
/ 15 апреля 2019

Консультируясь на форуме нашей Системы управления обучением, кажется, мы должны были прийти к чему-то вроде этого:

#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"
                        "1: xchgq (%0), %%rax\n\t"
                        "test %%rax, %%rax\n\t"
                        "jz 1b\n\t":"=r" (mutex) :"r"(mutex):"rax","cc","memory"
           );
}

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

В частности, нас попросили реализовать мьютекс в виде спин-блокировки (сначала изатем спин-блокировка с учетом кэша), это решает проблему, если рассматривать ее как упражнение.Однако, как указывалось выше, это не будет жизнеспособным в промышленности.

...