这是我的自旋锁的实现,但似乎不能保护关键代码。我的实现有问题吗?

static __inline__ int xchg_asm(int* lock, int val)
{
  int ret;
  __asm__ __volatile__(
    LOCK "movl (%1),%%eax;
    xchg (%1),%2;
    movl %%eax, %0" :"=m" (ret) :"d"(lock), "c"(val)
  );
  return ret;
}
void spin_init(spinlock_t* sl)
{
  sl->val = 0;
}
void spin_lock(spinlock_t* sl)
{
  int ret;
  do {
    ret = xchg_asm(&(sl->val), 1);
  } while ( ret==0 );
}

void spin_unlock(spinlock_t* sl)
{
  xchg_asm(&(sl->val), 0);
}

最佳答案

您的代码等于:

static __inline__ int xchg_asm(int* lock, int val) {
   int save_old_value_at_eax;

   save_old_value_at_eax = *lock;        /* with a wrong lock prefix */
   xchg *lock with val and discard the original value of *lock.
   return save_old_value_at_eax;           /* but it not the real original value of *lock */
}

从代码中可以看到,在CPU执行xchg时,save_old_value_at_eax不是真正的原始值。 应该在执行xchg 之前通过xchg指令而不是通过保存来获取旧值/原始值。 (“这不是真正的旧/原始值”表示,如果另一个CPU在该CPU保存该值之后但在该CPU执行xchg指令之前进行了锁定,则该CPU将获得错误的旧值,并且认为使用了锁定成功,因此,两个CPU同时进入CS。您已将一条read-modify-write指令分隔为三个指令,整个这三个指令不是原子的(即使您将锁前缀移动到xchg)。

我猜您以为锁定前缀将锁定整个三个指令,但实际上锁定前缀只能用于附加的唯一指令(不能附加所有指令)
而且,对于xchg,我们不需要SMP上的锁前缀。引用linux_kernel_src/arch/x86//include/asm/cmpxchg.h
/*
 * Note: no "lock" prefix even on SMP: xchg always implies lock anyway.
 * Since this is generally used to protect other memory information, we
 * use "asm volatile" and "memory" clobbers to prevent gcc from moving
 * information around.
 */

我的建议:
  • 不要重复自己,请使用Linux内核的自旋锁。
  • 不要重复自己,如果要实现自旋锁,请使用linux内核的xchg()和cmpxchg()。
  • 了解有关说明的更多信息。您还可以了解linux内核如何实现它。
  • 关于linux - 我的自旋锁有问题吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11515075/

    10-09 06:39