我正在尝试在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
}
}
}
答案 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。