Что-то не так с моей спин-блокировкой?

Вот моя реализация блокировки спина, но, похоже, она не может защитить критический код. Что-то не так с моей реализацией?

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);
}

Ответ 1

Ваш код равен:

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 */
}

Вы можете видеть из кода, save_old_value_at_eax не является реальным исходным значением, а процессор выполняет xchg. Вы должны получить старое/оригинальное значение командой xchg, а не сохранить его перед выполнением xchg. ( "это не реальное старое/оригинальное значение" означает, что если другой процессор берет блокировку после того, как этот CPU сохранит значение, но до того, как этот процессор выполнит инструкцию xchg, этот CPU получит неправильное старое значение, и он считает, что блокировка успешна, таким образом, одновременно в ЦПУ входят два ЦП). Вы разделили инструкцию read-modify-write на три команды, все три инструкции не являются атомарно (даже вы переместите префикс блокировки на xchg).

Я думаю, вы считали, что префикс блокировки заблокирует три инструкции WHOLE, но фактически префикс блокировки может использоваться только для единственной инструкции, к которой он присоединен (не все инструкции могут быть прикреплены) И нам не нужен префикс блокировки для SMP для xchg. Цитата из 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.
  • НЕ ПОВТОРИТЕ СЕБЯ, пожалуйста, используйте xchg(), cmpxchg() ядра linux, если вы хотите реализовать блокировку спина.
  • узнать больше о инструкциях. вы также можете узнать, как это реализует ядро ​​Linux.

Ответ 2

Я считаю, что проблема в том, что префикс инструкции блокировки применяется только к следующей инструкции, поэтому ваш обмен не является атомарным. Дополнительную информацию см. В этом ответе на SO: Что делает" lock " инструкция в сборке x86?

Я думаю, что если вы переместите префикс инструкции блокировки на xchg, он будет работать.

edit: Это может быть полезно (например, атомный обмен в сборке gcc): http://locklessinc.com/articles/locks/

Обратите внимание, что я считаю, что мой первоначальный ответ на самом деле неправильный, дальнейший поиск в Google показывает, что xchg заблокирован, если память ссылается автоматически с 386.