Атомарная операция

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск

Атомарная (атом от греч. atomos – неделимое) операция — операция, которая либо выполняется целиком, либо не выполняется вовсе; операция, которая не может быть частично выполнена и частично не выполнена. Атомарные операции используются в многопроцессорных компьютерах и в многозадачных операционных системах для обеспечения доступа нескольких процессов и/или нескольких потоков одного процесса к разделяемым между ними ресурсам. Атомарная операция выполняется только одним потоком.

Классификация[править | править вики-текст]

Атомарность операций может обеспечиваться аппаратно (аппаратурой) и программно (программным кодом). В первом случае используются особые машинные инструкции, атомарность выполнения которых гарантируется аппаратурой. Не все машинные инструкции атомарны. Во втором случае используются специальные программные средства — мьютексы, семафоры и др. (см. межпроцессное взаимодействие), с помощью которых осуществляется блокировка разделяемого ресурса; после блокировки выполняется операция, которую требуется выполнить атомарно. Блокировка представляет собой атомарную операцию, которая либо предоставляет ресурс в пользование потоку, либо сообщает потоку о том, что ресурс уже используется другим потоком или процессом (занят).

Ассемблерные инструкции и атомарность[править | править вики-текст]

Машинные инструкции, выполнение которых всегда можно считать атомарным:

  • машинные инструкции, получающие данные из регистров и записывающие данные в регистры (операции вида «операция регистр регистр») (считаются атомарными, так как регистры недоступны за пределами вычислительного ядра процессора);
  • машинные инструкции для чтения данных из памяти по выровненному адресу и записи их в регистр общего назначения;
  • машинные инструкции для чтения данных из регистра общего назначения и записи их в память по выровненному адресу;
  • машинные инструкции, специально созданные для работы атомарно и обычно называемые атомарными инструкциями.

Машинные инструкции, которые не являются атомарными:

  • машинные инструкции для чтения/записи данных по невыровненному адресу (выполняя одну из таких инструкций, процессор вынужден выполнить обращение к двум ячейкам памяти. В момент, когда процессор обращается к одной ячейке, другая ячейка может быть изменена другим процессором);
  • все машинные инструкции вида «чтение-модификация-запись»[источник?] (выполнение одной такой инструкции сводится к чтению данных из памяти, изменению данных в АЛУ и записи данных в память. После чтения данных из памяти содержимое памяти может измениться);
  • строковые машинные инструкции процессоров x86;
  • машинные инструкции push и pop процессоров x86;
  • машинные инструкции, работающие со специальными управляющими регистрами (подобные инструкции могут выполняться в течении нескольких процессорных тактов и порождать десятки или сотни обращений к памяти, используются только в системном программном обеспечении).

Атомарные инструкции процессоров x86[править | править вики-текст]

Атомарные инструкции процессоров архитектуры x86:

  • CMPXCHG, CMPXCHG8B, CMPXCHG16B — основная атомарная инструкция процессоров x86, выполняющая сравнение и обмен. При использовании с префиксом LOCK[1][2] атомарно сравнивает значение переменной с указанным значением и, в зависимости от результата сравнения, записывает в переменную указанное значение или ничего не делает. Является основой реализации всех безблокировочных алгоритмов, часто используется в реализации спинлоков, RWLock'ов и практически всех высокоуровневых синхронизирующих элементов, таких как семафоры, мьютексы, события и пр.;
  • XCHG — операция для чтения данных из регистра или памяти и записи данных в регистр или память. На процессорах x86 выполняется атомарно[3]. Часто используется в реализации спинлоков.

Кроме того, многие машинные инструкции вида «чтение-модификация-запись» выполняются атомарно при наличии префикса LOCK[4] (опкод 0xF0), например, следующие:

  • команды сложения и вычитания ADD, ADC, SUB и SBB в случае, если операнд-приёмник — адрес ячейки памяти;
  • команды инкремента и декремента INC и DEC;
  • логические команды AND, OR и XOR;
  • однооперандные команды NEG и NOT;
  • битовые операции BTS, BTR и BTC;
  • операция сложения и обмена XADD.

Префикс LOCK вызывает блокировку доступа к памяти на время выполнения инструкции. Блокировка может распространяться на область памяти шире, чем длина операнда, например, на длину линии кэша.

Атомарные инструкции процессоров RISC[править | править вики-текст]

Особенностью процессоров архитектур RISC является отсутствие инструкций вида «чтение-модификация-запись». В процессорах RISC с архитектурами DEC Alpha, PowerPC, MIPS и ARM (ARMv6 и старше) поддерживается механизм неблокирующего эксклюзивного доступа к памяти. Атомарные операции реализуются с использованием пары инструкций эксклюзивного чтения-записи LL и SC следующим образом:

  • загрузка с пометкой (LL — load linked);
  • изменение данных;
  • попытка записи (SC — store conditional).

Первая инструкция (LL) загружает данные из ячейки памяти в регистр и помечает ячейку, как ячейку для эксклюзивного доступа. Далее производятся необходимые изменения данных в регистре. Запись данных из регистра в память (SC) производится только в том случае, если значение ячейки памяти не менялось. Если значение менялось, три операции (LL, изменение данных и SC) следует повторить.

Атомарные инструкции и компиляторы[править | править вики-текст]

Компиляторы языков высокого уровня, как правило, не используют при генерации кода атомарные инструкции, поскольку, во-первых, атомарные операции во много раз более ресурсоёмкие, чем обычные, а во-вторых, у компилятора нет информации о том, когда доступ к данным должен осуществляться атомарно (так как даже модификатор volatile для переменной в языках C/C++ не означает реальной необходимости применения атомарных операций). В случае необходимости программист может использовать атомарные инструкции одним из следующих способов:

  1. вставить атомарные инструкции в код, используя предоставляемый компилятором ассемблер, например, GCC Inline Assembly компилятора gcc;
  2. использовать функции, предоставляемые компилятором и вызывающие атомарные инструкции, например, функции семейств __builtin_ или __sync_ компилятора gcc;
  3. использовать функции, предоставляемые библиотеками и вызывающие атомарные инструкции, например, функции библиотеки Glib;
  4. использовать языки программирования, поддерживающие атомарность, например, языки стандартов C11 и C++14, поддерживающие типы _Atomic и atomic и функции семейства atomic_[5].

См. также[править | править вики-текст]

Примечания[править | править вики-текст]

  1. CMPXCHG - Compare and exchange.
  2. CMPXCHG8B - Compare and exchange 8 bytes.
  3. http://faydoc.tripod.com/cpu/xchg.htm "If a memory operand is referenced, the processor's locking protocol is automatically implemented for the duration of the exchange operation, regardless of the presence or absence of the LOCK prefix or of the value of the IOPL."
  4. Атомарные операции. История вопроса
  5. Atomic operations library - cppreference.com

Ссылки[править | править вики-текст]