Является x86 CMPXCHG атомом?

Документация Intel на

http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf

говорит

"Эта инструкция может использоваться с префиксом LOCK, чтобы позволить инструкции выполняться атомарно."

Мой вопрос

  • Может ли CMPXCHG работать с адресом памяти? Из документа, похоже, нет, но может ли кто-нибудь подтвердить, что работает только с фактическим VALUE в регистре, а не с адресом памяти?

  • Если CMPXCHG не является атомарным, а уровень уровня CAS на высоком уровне должен быть реализован через LOCK CMPXCHG (с префиксом LOCK), какова цель введения такой инструкции вообще?

Ответ 1

Вы смешиваете высокоуровневые блокировки с низкоуровневой функцией ЦП, которая называлась LOCK.

Блокировки на высоком уровне, которые избегают алгоритмы блокировки, могут защитить произвольные фрагменты кода, выполнение которых может занять произвольное время, и, таким образом, эти блокировки должны будут помещать потоки в состояние ожидания до тех пор, пока не будет доступна блокировка, что является дорогостоящей операцией, например подразумевает поддержание очереди ожидающих потоков.

Это совсем другое, чем функция префикса CPU LOCK, которая защищает только одну команду и, таким образом, может удерживать другие потоки только на протяжении одной только этой инструкции. Поскольку это реализовано самим процессором, для этого не требуются дополнительные усилия по созданию программного обеспечения.

Поэтому задача разработки алгоритмов блокировки не является полностью устранением синхронизации, она сводится к тому, чтобы уменьшить критический раздел кода до одной атомной операции, которая будет предоставляться самим процессором.

Ответ 2

Похоже, что вы действительно спрашиваете:

Почему префикс lock неявный для cmpxchg с операндом памяти, как это для xchg?

Простой ответ (что другие дали) - это просто, что Intel разработала его таким образом. Но это приводит к вопросу:

Почему Intel это сделала? Существует ли прецедент для cmpxchg без lock?

В системе с одним процессором cmpxchg является атомарным относительно других потоков или любого другого кода, работающего на одном ядре ЦП. (Но не для "системных" наблюдателей, таких как устройство ввода-вывода с отображением памяти или устройство, которое DMA считывает нормальную память, поэтому lock cmpxchg имеет значение даже для однопроцессорных схем процессора).

Контекстные коммутаторы могут выполняться только на прерываниях, а прерывания происходят до или после инструкции, а не в середине. Любой код, запущенный на одном CPU, увидит cmpxchg как полностью выполненный или вообще не выполненный.


Например, ядро ​​Linux обычно скомпилировано с поддержкой SMP, поэтому оно использует lock cmpxchg для атомарного CAS. Но при загрузке в однопроцессорной системе он будет исправлять префикс lock до nop везде, где этот код был встроен, поскольку nop cmpxchg работает намного быстрее, чем lock cmpxchg. Для получения дополнительной информации см. Эту статью статью LWN о системе Linux "SMP alternatives" . Он даже может вернуться к префиксам lock перед подключением второго процессора к другому.

Подробнее об атомарности отдельных инструкций для однопроцессорных систем в этом ответе и в @supercat ответить + комментарии on Can num++ будет атомарным для int num. См. мой ответ там для получения подробной информации о том, как атомичность действительно работает/реализована для команд read-modify-write, таких как lock cmpxchg.


(Это же рассуждение применимо и к cmpxchg8b/cmpxchg16b и xadd, которое обычно используется только для синхронных/атомных операций, а не для того, чтобы однопоточный код работал быстрее. Очевидно, место назначения памяти add [mem], reg полезен вне случая lock add [mem], reg.)

Ответ 3

Префикс LOCK предназначен для блокировки доступа к памяти для текущей команды, так что другие команды, находящиеся в конвейере CPU, могут получить доступ к памяти в это время. Используя префикс LOCK, выполнение команды не прерывается другой командой в конвейере CPU из-за доступа к памяти других команд, которые выполняются одновременно. Руководство INTEL сообщает:

Префикс LOCK может быть добавлен только к следующим инструкциям и только в тех формах инструкций, где пункт назначения операнд - операнд памяти: ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCH8B, CMPXCHG16B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD и XCHG. Если префикс LOCK используется с одной из этих инструкций и операнд источника является операндом памяти, исключение опкода undefined(#UD).