CMPXCHG не атомарен в этой программе NASM - PullRequest
0 голосов
/ 05 мая 2019

Я реализовал спин-блокировку в NASM-64 для Windows.Я использую спин-блокировку для блокировки буфера общей памяти, чтобы каждое ядро ​​записывало в общий буфер по одному ядру за раз (ядро 0 сначала, ядро ​​1 секунда, ядро ​​2 третье и т. Д.). Насколько я знаюмьютекс или семафор не позволят мне выполнять буферизацию в основном порядке, и спин-блокировка предпочтительнее, потому что она не использует вызов ОС.

Вот этот раздел кода.Это не полный пример, потому что проблема в этом небольшом разделе гораздо большей программы сборки.Я использовал cmpxchg для атомарности.

При входе в этот раздел rax содержит номер ядра, а rbx - переменную памяти spinlock_core, которая устанавливается на ноль (первое ядро) при входе в этот раздел.После того как каждое ядро ​​закончено, spinlock_core увеличивается до следующего номера ядра.

mov rdi,Test_Array
movq rbx,xmm11
mov [rdi+rax],rbx ; rax contains the core number offset (0, 8, 16, 24)
push rax

; Spin Lock
spin_lock_01:
lock cmpxchg [spinlock_core],rax ; spinlock_core is set to zero on first entry
jnz spin_lock_01

; To test the result:
mov rdi,Test_Array
mov [rdi+rax+32],rax
jmp out_of_here

Результаты этого теста находятся в Test_Array, который заполняется количеством байтов для записи для каждого ядра.По возвращении он содержит:

40, 40, 40, 16, 0, 8, 16, 24

, показывающий, что ядро ​​0-2 имеет 40 байтов для записи, а ядро ​​3 имеет 16 байтов для записи.Однако последние четыре элемента Test_Array содержат смещение ядра для каждого из четырех ядер.Если спин-блокировка работала правильно (cmpxchg rax, rbx), последние четыре элемента должны содержать ноль, показывая, что пропущено только первое ядро.Но это показывает, что все четыре ядра были пропущены.

Я предполагаю, что мой cmpxchg не является атомарным, и поэтому другие ядра просачиваются сквозь него - они должны быть разрешены только при увеличении spinlock_core до следующего ядра, но этого не происходит до того, как мы выйдем с jmpотсюда.Согласно документам, cmpxchg должен предшествовать префикс «lock», как в блокировке cmpxchg rax, rbx, но когда я это делаю, ассемблер NASM говорит «предупреждение: инструкция не блокируется [-w + lock]».Сайт Феликса Клутье говорит, что префикс блокировки необходим только в том случае, если задействован операнд памяти, но когда я пишу «cmpxchg rax, [spinlock_core]», я получаю «недопустимую комбинацию кодов операций и операндов».

Подводя итог, я могу задать следующие вопросы: почему cmpxchg не является атомарным, как написано выше, и почему ассемблер NASM не позволяет использовать префикс блокировки?

Существует множество подробных сообщений о переполнении стека и в других местах по вопросу атомарности, но я не нашел ни одного, который бы касался этой конкретной проблемы.

Спасибо за любую помощь.

1 Ответ

0 голосов
/ 06 мая 2019

Вот как я решил это с помощью комментариев ниже от @Peter Cordes и @prl.Изменения: (1) использование bx, а не ax в качестве регистра назначения и (2) использование только младших 8-битных регистров ax и bx, а не rax и rbx.

xor rbx,rbx
spin_lock_01:
pop rax ; the core number is stored in the stack
mov bl,al ; put core number into bx
mov rcx,rax ; preserve core number for later use
push rax ; push core number back to stack
lock cmpxchg [spinlock_core],bl ; spinlock_core is set to zero on first entry
jnz spin_lock_01

; now test it
mov rdi,Test_Array
mov rbx,[spinlock_core]
mov rdx,[order_of_execution]
add rdx,1
mov [order_of_execution],rdx
mov [rdi+rcx+0],rcx
mov [rdi+rcx+32],rdx
add rbx,8
mov [spinlock_core],rbx
jmp out_of_here

Вывод в Test_Array показываеткак: 0,8,16,24,1,2,3,4.Это показывает, что каждое из ядер пропущено в порядке ядра (1,2,3,4).Расположение памяти spinlock_core увеличивается на каждое ядро ​​по завершении, сигнализируя следующее ядро.

...