在C中实现互斥锁

时间:2010-04-21 01:51:40

标签: c assembly mutex

我正在尝试在C中创建一个非常简单的自旋锁互斥锁,并且由于某种原因我得到两个线程同时获得锁定的情况,这是不可能的。它运行在多处理器系统上,这可能是出现问题的原因。任何想法为什么它不起作用?

void mutexLock(mutex_t *mutexlock, pid_t owner)
{
int failure = 1;
while(mutexlock->mx_state == 0 || failure || mutexlock->mx_owner != owner)
{
    failure = 1;
    if (mutexlock->mx_state == 0)
    {
        asm(
        "movl    $0x01,%%eax\n\t"      // move 1 to eax
        "xchg    %%eax,%0\n\t"         // try to set the lock bit
        "mov     %%eax,%1\n\t"         // export our result to a test var
        :"=r"(mutexlock->mx_state),"=r"(failure)
        :"r"(mutexlock->mx_state)
        :"%eax"
        );
    }
    if (failure == 0)
    {
        mutexlock->mx_owner = owner; //test to see if we got the lock bit
    }
    } 
}

2 个答案:

答案 0 :(得分:7)

首先,您在第一次执行failure条件时测试未初始化的变量(while())。

你的实际问题是你告诉gcc使用mx_state的注册表 - 这显然不适用于自旋锁。尝试:

    asm volatile (
    "movl    $0x01,%%eax\n\t"      // move 1 to eax
    "xchg    %%eax,%0\n\t"         // try to set the lock bit
    "mov     %%eax,%1\n\t"         // export our result to a test var
    :"=m"(mutexlock->mx_state),"=r"(failure)
    :"m"(mutexlock->mx_state)
    :"%eax"
    );

请注意asm volatile在这里也很重要,以确保它不会从你的while循环中提升。

答案 1 :(得分:3)

问题是您将mx_state加载到寄存器('r'约束)然后与寄存器进行交换,只将结果写回asm代码末尾的mx_state。你想要的更像是什么

asm(
    "movl    $0x01,%%eax\n\t"      // move 1 to eax
    "xchg    %%eax,%1\n\t"         // try to set the lock bit
    "mov     %%eax,%0\n\t"         // export our result to a test var
    :"=r"(failure)
    :"m" (mutexlock->mx_state)
    :"%eax"
    );

即使这有点危险,因为理论上编译器可以加载mx_state,将其溢出到本地临时堆栈槽中,并在那里执行xchg。它也有点低效,因为它有额外的硬编码,可能不需要,但优化器无法消除。你最好使用更简单的asm扩展为单个指令,例如

failure = 1;
asm("xchg  %0,0(%1)" : "=r" (failure) : "r" (&mutex->mx_state), "0" (failure));

请注意我们如何通过使用mx_state的地址而不是其值来强制使用mx_state。