Чтение онлайн

ЖАНРЫ

1.Внутреннее устройство Windows (гл. 1-4)
Шрифт:
Спин-блокировки

Механизм, применяемый ядром для взаимоисключения в многопроцессорных системах, называется спин-блокировкой (spinlock). Спин-блокировка — это блокирующий примитив, сопоставленный с какой-либо глобальной структурой данных вроде очереди DPC (рис. 3-24).

Перед входом в любую из критических секций, показанных на рис. 3-24, ядро должно установить спин-блокировку, связанную с защищенной очередью DPC Если спин-блокировка пока занята, ядро продолжает попытки установить спин-блокировку до тех пор, пока не достигнет успеха. Термин получил такое

название из-за поведения ядра (и соответственно процессора), которое «крутится» (spin) в цикле, повторяя попытки, пока не захватит блокировку.

Спин-блокировки, как и защищаемые ими структуры данных, находятся в глобальной памяти. Код для их установки и снятия написан на языке ассемблера для максимального быстродействия. Bo многих архитектурах спин-блокировка реализуется аппаратно поддерживаемой командой test-and-set, которая проверяет значение переменной блокировки и устанавливает блокировку, выполняя всего одну атомарную команду. Это предотвращает захват блокировки вторым потоком в промежуток между проверкой переменной и установкой блокировки первым потоком.

Всем спин-блокировкам режима ядра в Windows назначен IRQL, всегда соответствующий уровню «DPC/dispatch» или выше. Поэтому, когда поток пытается установить спин-блокировку, все действия на этом или более низком уровне IRQL на данном процессоре прекращаются. Поскольку диспетчеризация потоков осуществляется при уровне «DPC/dispatch», поток, удерживающий спин-блокировку, никогда не вытесняется, так какданный IRQL маскирует механизмы диспетчеризации. Такая маскировка не дает прервать выполнение критической секции кода под защитой спин-блокировки и обеспечивает быстрое ее снятие. Спин-блокировки используются в ядре с большой осторожностью и устанавливаются на минимально возможное время.

ПРИМЕЧАНИЕ Поскольку IRQL — достаточно эффективный механизм синхронизации для однопроцессорных систем, функции установки и снятия спин-блокировки в однопроцессорных версиях HAL на самом деле просто повышают и понижают IRQL.

Ядро предоставляет доступ к спин-блокировкам другим компонентам исполнительной системы через набор функций ядра, включающий KeAcqui-reSpinlock и KeReleaseSpinlock. Например, драйверы устройств требуют спин-блокировки, чтобы система гарантировала единовременный доступ к регистрам устройства и другим глобальным структурам данных со стороны лишь одной части драйвера (и только с одного процессора). Спин-блокировка не предназначена для пользовательских программ — они должны оперировать объектами, которые рассматриваются в следующем разделе.

Спин-блокировки ядра накладывают ограничения на использующий их код. Как уже отмечалось, их IRQL всегда равен «DPC/dispatch», поэтому установивший спин-блокировку код может привести к краху системы, если попытается заставить планировщик выполнить операцию диспетчеризации или вызовет ошибку страницы.

Спин-блокировки с очередями

B некоторых ситуациях вместо стандартной спин-блокировки применяется особый тип спин-блокировки — с очередью (queued spinlock). Спин-блокировка с очередью лучше масштабируется в многопроцессорных системах, чем стандартная. Как правило, Windows использует лишь стандартные спин-блокировки, когда конкуренция за спин-блокировку ожидается низкой.

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

Тот факт, что спин-блокировка с очередью устанавливает флаги, а не глобальные

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

Windows определяет ряд глобальных спин-блокировок с очередями, сохраняя указатели на них в массиве, который содержится в блоке PCR (processor control region) каждого процессора. Глобальную спин-блокировку можно получить вызовом KeAcquireQueuedSpinlock с индексом в массиве PCR, по которому сохранен указатель на эту спин-блокировку. Количество глобальных спин-блокировок растет по мере появления новых версий операционной системы, и таблица их индексов публикуется в заголовочном файле Ntddk.h, поставляемом с DDK.

ЭКСПЕРИМЕНТ: просмотр глобальных спин-блокировок с очередями

Вы можете наблюдать за состоянием глобальных спин-блокировок с очередями, используя команду !qlock отладчика ядра. Эта команда имеет смысл лишь в многопроцессорной системе, так как в однопроцессорной версии HAL спин-блокировки не реализованы. B следующем примере (подготовленном в Windows 2000) спин-блокировка с очередью для базы данных диспетчера ядра удерживается процессором номер 1, а остальные спин-блокировки этого типа не затребованы (о базе данных диспетчера ядра см. главу 6).

Внутристековые спин-блокировки с очередями

Помимо статических спин-блокировок с очередями, определяемых глобально, ядра Windows XP и Windows Server 2003 поддерживают динамически создаваемые спин-блокировки с очередями. Для их создания предназначены функции KeAcquireInStackQueuedSpinlock и KeReleaseInStackQueuedSpin-lock. Этот тип блокировок используется несколькими компонентами, в том числе диспетчером кэша, диспетчером пулов исполнительной системы (executive pool manager) и NTFS. Упомянутые функции документированы в DDK для сторонних разработчиков драйверов.

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

Взаимоблокирующие операции в исполнительной системе

Ядро предоставляет ряд функций синхронизации, использующих спин-блокировки для более сложных операций, например для добавления и удаления элементов из одно- и двунаправленных связанных списков. K таким функциям, в частности, относятся ExfoterlockedPopEntryList и ExInterlockedPushEntryList (для однонаправленных связанных списков), ExInterlockedInsertHeadList и ExInter-lockedRemoveHeadList (ддя двунаправленных связанных списков). Все эти функции требуют передачи стандартной спин-блокировки в качестве параметра и интенсивно используются в ядре и драйверах устройств.

Синхронизация при низком IRQL

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

Поделиться с друзьями: