Подтвердить что ты не робот

Барьер памяти и atomic_t на linux

В последнее время я читаю некоторые коды пространства ядра Linux, я вижу это

uint64_t used;
uint64_t blocked;

used = atomic64_read(&g_variable->used);       //#1
barrier();                                     //#2
blocked = atomic64_read(&g_variable->blocked); //#3

Какова семантика этого фрагмента кода? Обеспечивает ли он выполнение # 1 перед # 3 на # 2. Но я немного помешал, потому что

#A В 64-битной платформе макрос Atom64_read расширяется до

used = (&g_variable->used)->counter           // where counter is volatile.

В 32-битной платформе она была преобразована для использования блокировки cmpxchg8b. Я предполагаю, что эти два имеют одинаковую семантику, а для 64-битной версии, я думаю, это означает:

  • all-or-nothing, мы можем исключить случай, когда адрес не выравнивается и размер слова больше, чем размер родного слова процессора.
  • не оптимизировать, заставить процессор считывать данные из памяти.

atomic64_read не имеет семантики для сохранения порядка чтения!!! см. this

#B макрос барьера определяется как

/* Optimization barrier */
/* The "volatile" is due to gcc bugs */
#define barrier() __asm__ __volatile__("": : :"memory")

Из wiki this просто запрещает gcc компилятор переупорядочивать чтение и запись.

Что меня путают, так это то, как отключить оптимизацию переупорядочения для CPU? Кроме того, могу ли я думать, что барьерный макрос является полным забором?

4b9b3361

Ответ 1

32-разрядные процессоры x86 не обеспечивают простые операции чтения атома для 64-битных типов. Единственная атомная операция на 64-битных типах на таких процессорах, которая имеет дело с "нормальными" регистрами, - это LOCK CMPXCHG8B, поэтому она используется здесь. Альтернативой является использование регистров MOVQ и MMX/XMM, но это требует знания состояния/регистров FPU и требует, чтобы все операции над этим значением выполнялись с инструкциями MMX/XMM.

В 64-разрядных процессорах x86_64 выровненные чтения 64-битных типов являются атомарными и могут выполняться с помощью инструкции MOV, поэтому требуется только обычное чтение --- использование volatile - это просто убедитесь, что компилятор действительно выполняет чтение и не кэширует предыдущее значение.

Что касается упорядочения чтения, встроенный ассемблер, который вы указываете, гарантирует, что компилятор выдает команды в правильном порядке, и это все, что требуется для процессоров x86/x86_64, при условии, что записи правильно упорядочены. LOCK ed записи на x86 имеют полное упорядочение; plain MOV write обеспечивает "причинную согласованность", поэтому, если поток A делает x=1, а затем y=2, если поток B читает y==2, то последующее чтение x будет видеть x==1.

В IA-64, PowerPC, SPARC и других процессорах с более расслабленной моделью памяти может быть больше atomic64_read() и barrier().

Ответ 2

Процессоры

x86 не выполняют переупорядочение после чтения, поэтому достаточно предотвратить компилятор от какого-либо переупорядочения. На других платформах, таких как PowerPC, все будет выглядеть по-другому.