- •Лекция №11. Механизмы синхронизации
- •[11.1] Спин-блокировки
- •[11.1.1] Использование обычных спин-блокировок
- •[11.1.1.2] Проблема взаимоблокировок (deadlocks)
- •[11.2] Диспетчерские объекты
- •[11.2.1] Ожидание (захват) диспетчерских объектов
- •[11.2.2] Мьютексы ядра
- •[11.2.3] Семафоры
- •[11.2.4] События (events)
- •[11.3] Быстрые мьютексы
- •[11.4] Ресурсы Исполнительной системы
- •[11.5] Подведение итогов
[11.2.1] Ожидание (захват) диспетчерских объектов
Как уже было сказано, каждый диспетчерский объект всегда находится в одном из двух состояний – сигнальном (свободном) или несигнальном (занятым). Термины “свободный” и “занятый” довольно вольные, поэтому лучше использовать термины сигнальный и несигнальный. Для ожидания момента перехода объекта из несигнального в сигнальное состояние служат специальные функции ожидания: KeWaitForSingleObject() и KeWaitForMultipleObjects(). Важной особенностью этих функций служит то, что в качестве одного из их параметров указывается интервал времени, в течение которого необходимо ждать.
Если указан нулевой интервал времени (но не NULL !!!), вызов функции ожидания не блокирует поток. В этом случае она работает как функция проверки состояния диспетчерского объекта и может быть вызвана на уровне IRQL<=DISPATCH_LEVEL.
Если интервал времени не указан (NULL в качестве параметра), или указан ненулевой интервал времени, функции ожидания можно вызывать на уровне IRQL строго<DISPATCH_LEVEL. В противном случае будет сделана попытка блокирования потока, но, как мы говорили раньше, механизм диспетчеризации на уровнях IRQL >= DISPATCH_LEVEL не работает. Переключение контекста потока не сможет произойти, и функция ожидания завершит работу, как будто ожидаемый диспетчерский объект находится в сигнальном состоянии.
Отличие функции KeWaitForMultipleObjects() от KeWaitForSingleObject() в том, что она может ожидать перехода в сигнальное состояние сразу всех указанных в ней диспетчерских объектов, либо любого одного из них.
[11.2.2] Мьютексы ядра
Слово Мьютекс (mutex = Mutually EXclusive) означает взаимоисключение, т.е. мьютекс обеспечивает нескольким потокам взаимоисключающий доступ к совместно используемому ресурсу.
Вначале отметим , что кроме мьютексов ядра, есть еще быстрые мьютексы, являющиеся объектами исполнительной системы и не являющиеся диспетчерскими объектами. Мьютексы ядра обычно называют просто мьютексами.
Мьютексы ядра – это диспетчерские объекты – эквиваленты спин-блокировок. Двумя важными отличиями мьютексов от спин-блокировок являются:
захват мьютекса является уникальным в рамках конкретного контекста потока. Поток, в контексте которого произошел захват мьютекса, является его владельцем, и может впоследствии рекурсивно захватывать его. Драйвер, захвативший мьютекс в конкретном контексте потока, обязан освободить его в том же контексте потока, нарушение этого правила приведет к появлению “синего экрана”.
Для мьютексов предусмотрен механизм исключения взаимоблокировок (см. п. [11.1.1.2]). Он заключается в том, что при инициализации мьютекса функцией KeInitializeMutex() указывается уровень (level) мьютекса. Если потоку требуется захватить несколько мьютексов одновременно, он должен делать это в порядке возрастания значения level.
Функции работы с мьютексами ядра
VOID KeInitializeMutex(IN PKMUTEX Mutex, IN ULONG Level);
Инициализация мьютекса. Память под мьютекс уже должна быть выделена. После инициализации мьютекс находится в сигнальном состоянии.
LONG KeReleaseMutex(IN PKMUTEX Mutex, IN BOOLEAN Wait);
Освобождение мьютекса, с указанием того, последует ли сразу после этого вызов функции ожидания мьютекса. Если параметр Wait равен TRUE, сразу за вызовом KeReleaseMutex() должен следовать вызов одной из функций ожидания KeWaitXxx() (см. п. [11.2.1]). В этом случае гарантируется, что пара функций – освобождение мьютекса и ожидание – будет выполнена как одна операция, без возможного в противном случае переключения контекста потока. Возвращаемым значением будет 0, если мьютекс был освобожден, т.е. переведен из несигнального состояния в сигнальное. В противном случае возвращается ненулевое значение.
LONG KeReadStateMutex(IN PKMUTEX Mutex);
Возвращает состояние мьютекса – сигнальное или несигнальное.