Spinlock - Spinlock

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

Потому что они избегают накладных расходов от Операционная система изменение графика процесса или же переключение контекста, спин-блокировки эффективны, если потоки могут быть заблокированы только на короткие периоды времени. По этой причине, ядра операционной системы часто используют спин-блокировки. Однако спин-блокировки становятся бесполезными, если они удерживаются в течение более длительного времени, поскольку они могут препятствовать запуску других потоков и требовать перепланирования. Чем дольше поток удерживает блокировку, тем выше риск того, что поток будет прерван планировщиком ОС, удерживая блокировку. Если это произойдет, другие потоки останутся «вращающимися» (неоднократно пытающимися получить блокировку), в то время как поток, удерживающий блокировку, не продвигается к ее освобождению. В результате откладывается на неопределенное время, пока поток, удерживающий блокировку, не закончит и не освободит ее. Это особенно верно в однопроцессорной системе, где каждый ожидающий поток с одинаковым приоритетом, вероятно, будет тратить свой квант (выделенное время, в течение которого поток может работать) на вращение до тех пор, пока поток, удерживающий блокировку, наконец не завершится.

Правильная реализация спин-блокировок создает проблемы, потому что программисты должны учитывать возможность одновременного доступа к блокировке, что может вызвать условия гонки. Как правило, такая реализация возможна только при наличии специальных язык ассемблера инструкции, такие как атомный испытать и установить операции и не могут быть легко реализованы на языках программирования, не поддерживающих действительно атомарные операции.[1] На архитектурах без таких операций или если требуется реализация на языке высокого уровня, может использоваться алгоритм неатомарной блокировки, например Алгоритм Петерсона. Однако для такой реализации может потребоваться больше объем памяти чем спин-блокировка, быть медленнее, чтобы обеспечить прогресс после разблокировки, и может быть не реализовано на языке высокого уровня, если внеочередное исполнение позволено.

Пример реализации

В следующем примере для реализации спин-блокировки используется язык ассемблера x86. Работает на любом Intel 80386 совместимый процессор.

; Синтаксис Intelзаблокировано:                      ; Переменная блокировки. 1 = заблокировано, 0 = разблокировано.     дд      0spin_lock:     mov     eax, 1          ; Установите регистр EAX на 1.     xchg    eax, [заблокирован]   ; Атомно поменяйте местами регистр EAX с                             ; переменная блокировки.                             ; Это всегда будет хранить 1 в замке, оставляя                             ; предыдущее значение в регистре EAX.     тест    eax, eax        ; Протестируйте EAX на себе. Помимо прочего, это будет                             ; установите нулевой флаг процессора, если EAX равен 0.                             ; Если EAX равен 0, значит, блокировка разблокирована и                             ; мы просто заперли его.                             ; В противном случае EAX равен 1, и мы не получили блокировку.     jnz     зрin_lock       ; Вернитесь к инструкции MOV, если установлен нулевой флаг.                             ; не установлено; замок был ранее заблокирован, и поэтому                             ; нам нужно вращать, пока он не разблокируется.     Ret                     ; Блокировка получена, вернитесь к звонку                             ; функция.spin_unlock:     xor     eax, eax        ; Установите регистр EAX в 0.     xchg    eax, [заблокирован]   ; Атомно поменяйте местами регистр EAX с                             ; переменная блокировки.     Ret                     ; Блокировка снята.

Значительные оптимизации

Приведенная выше простая реализация работает на всех процессорах, использующих архитектуру x86. Однако возможен ряд оптимизаций производительности:

В более поздних реализациях архитектуры x86 spin_unlock можно безопасно использовать разблокированный MOV вместо более медленного заблокированного XCHG. Это связано с тонким порядок памяти правила, которые поддерживают это, даже если MOV не является полным барьер памяти. Однако некоторые процессоры (некоторые Cyrix процессоров, некоторые доработки Intel Pentium Pro (из-за ошибок) и ранее Pentium и i486 SMP systems) будут делать неправильные действия, и данные, защищенные блокировкой, могут быть повреждены. На большинстве архитектур, отличных от x86, необходимо использовать явный барьер памяти или атомарные инструкции (как в примере). В некоторых системах, например IA-64, есть специальные инструкции "разблокировки", которые обеспечивают необходимый порядок памяти.

Чтобы уменьшить межпроцессорное автобусное движение, код, пытающийся получить блокировку, должен зацикливаться на чтении, не пытаясь ничего записать, до тех пор, пока не прочитает измененное значение. Потому что MESI протоколы кэширования, это приводит к тому, что строка кэша для блокировки становится «общей»; то есть замечательно нет трафик шины, пока ЦП ожидает блокировки. Эта оптимизация эффективна для всех архитектур ЦП, у которых есть кэш на ЦП, потому что MESI настолько широко распространен. На процессорах с Hyper-Threading приостановка с помощью представитель nop дает дополнительную производительность, намекая ядру, что оно может работать с другим потоком, пока блокировка вращается в ожидании.[2]

Расширения транзакционной синхронизации и другое оборудование транзакционная память Наборы команд в большинстве случаев служат для замены замков. Хотя блокировки по-прежнему требуются в качестве запасного варианта, они могут значительно повысить производительность за счет обработки процессором целых блоков атомарных операций. Эта функция встроена в некоторые реализации мьютексов, например в glibc. Аппаратная блокировка Elision (HLE) в x86 - это ослабленная, но обратно совместимая версия TSE, и мы можем использовать ее здесь для блокировки без потери совместимости. В этом конкретном случае процессор может отказаться от блокировки до тех пор, пока два потока не конфликтуют друг с другом.[3]

Более простая версия теста может использовать cmpxchg инструкция на x86, или __sync_bool_compare_and_swap встроен во многие компиляторы Unix.

С примененными оптимизациями образец будет выглядеть так:

; В C: while (! __ sync_bool_compare_and_swap (& заблокировано, 0, 1)) while (заблокировано) __builtin_ia32_pause ();spin_lock:    mov     ecx, 1             ; Установите регистр ECX на 1.повторить попытку:    xor     eax, eax           ; Обнулите EAX, потому что cmpxchg сравнивается с EAX.    XACQUIRE замок cmpxchg ecx, [заблокирован]                               ; атомарно решить: если блокировка равна нулю, записать в него ECX.                               ; XACQUIRE намекает процессору, что мы получаем блокировку.    je      из                ; Если мы заблокировали его (старое значение, равное EAX: 0), вернемся.Пауза:    mov     eax, [заблокирован]      ; Чтение заблокировано в EAX.    тест    eax, eax           ; Выполните нулевой тест, как и раньше.    jz      повторить попытку              ; Если это ноль, мы можем повторить попытку.    представитель нет                    ; Сообщите процессору, что мы ждем во спин-петле, чтобы он мог                               ; работайте сейчас над другим потоком. Также пишется как «пауза».    jmp     Пауза              ; Продолжайте делать чек-паузу.из:    Ret                        ; Все сделано.spin_unlock:    XRELEASE mov [заблокирован], 0   ; Предполагая, что применяются правила упорядочивания памяти, отпустите                                ; переменная блокировки с подсказкой «разблокировка блокировки».    Ret                        ; Блокировка снята.

Альтернативы

Основной недостаток спин-блокировки заключается в том, что ожидающий Чтобы получить замок, нужно потратить время, которое можно было бы продуктивно потратить в другом месте. Избежать этого можно двумя способами:

  1. Не приобретайте замок. Во многих ситуациях можно разработать структуры данных, которые не требуют блокировки, например используя данные для каждого потока или процессора и отключив прерывает.
  2. Выключатель в другой поток во время ожидания. Обычно это включает присоединение текущего потока к очереди потоков, ожидающих блокировки, с последующим переключением на другой поток, готовый выполнить некоторую полезную работу. Эта схема также имеет то преимущество, что она гарантирует, что ресурсный голод не происходит до тех пор, пока все потоки в конечном итоге откажутся от приобретенных ими блокировок и можно будет принять решение о планировании того, какой поток должен выполняться первым. Спиновые блокировки, которые никогда не требуют переключения, могут использоваться операционные системы реального времени, иногда называют сырые спин-блокировки.[4]

Большинство операционных систем (включая Солярис, Mac OS X и FreeBSD ) используют гибридный подход под названием «адаптивный мьютекс ". Идея состоит в том, чтобы использовать спин-блокировку при попытке доступа к ресурсу, заблокированному текущим потоком, но для перехода в спящий режим, если нить в настоящее время не работает. (Последний всегда случай в однопроцессорных системах.)[5]

OpenBSD попытался заменить спин-блокировки на билетные замки который заставил первым пришел-первым вышел поведение, однако это привело к большей загрузке ЦП в ядре и более крупных приложениях, таких как Fire Fox, становится намного медленнее.[6][7]

Смотрите также

Рекомендации

  1. ^ Зильбершац, Авраам; Гэлвин, Питер Б. (1994). Понятия операционной системы (Четвертое изд.). Эддисон-Уэсли. С. 176–179. ISBN  0-201-59292-4.
  2. ^ "gcc - спин-блокировка x86 с использованием cmpxchg". Переполнение стека.
  3. ^ «Новые технологии в архитектуре руки» (PDF).
  4. ^ Джонатан Корбет (9 декабря 2009 г.). "Именование Spinlock разрешено". LWN.net. Получено 14 мая 2013.
  5. ^ Зильбершац, Авраам; Гэлвин, Питер Б. (1994). Понятия операционной системы (Четвертое изд.). Эддисон-Уэсли. п. 198. ISBN  0-201-59292-4.
  6. ^ Тед Унангст (01.06.2013). "src / lib / librthread / rthread.c - Версия 1.71".
  7. ^ Тед Унангст (06.05.2016). "комментарий tedu к блокировке в WebKit - Lobsters".

внешняя ссылка