Мьютексы ядра

Ожидание (захват) диспетчерских объектов

Как уже было сказано, каждый диспетчерский объект всегда находится в одном из двух состояний – сигнальном (свободном) или несигнальном (занятым). Термины “свободный” и “занятый” довольно вольные, поэтому лучше использовать термины сигнальный и несигнальный. Для ожидания момента перехода объекта из несигнального в сигнальное состояние служат специальные функции ожидания: KeWaitForSingleObject() и KeWaitForMultipleObjects(). Важной особенностью этих функций служит то, что в качестве одного из их параметров указывается интервал времени, в течение которого необходимо ждать.

Если указан нулевой интервал времени (но не NULL!!!), вызов функции ожидания не блокирует поток. В этом случае она работает как функция проверки состояния диспетчерского объекта и может быть вызвана на уровне IRQL<=DISPATCH_LEVEL.

Если интервал времени не указан (NULL в качестве параметра), или указан ненулевой интервал времени, функции ожидания можно вызывать на уровне IRQL строго<DISPATCH_LEVEL. В противном случае будет сделана попытка блокирования потока, но, как мы говорили раньше, механизм диспетчеризации на уровнях IRQL >= DISPATCH_LEVEL не работает. Переключение контекста потока не сможет произойти, и функция ожидания завершит работу, как будто ожидаемый диспетчерский объект находится в сигнальном состоянии.

Отличие функции KeWaitForMultipleObjects() от KeWaitForSingleObject() в том, что она может ожидать перехода в сигнальное состояние сразу всех указанных в ней диспетчерских объектов, либо любого одного из них.

Слово Мьютекс (mutex = Mutually EXclusive) означает взаимоисключение, т.е. мьютекс обеспечивает нескольким потокам взаимоисключающий доступ к совместно используемому ресурсу.

Вначале отметим, что кроме мьютексов ядра, есть еще быстрые мьютексы, являющиеся объектами исполнительной системы и не являющиеся диспетчерскими объектами. Мьютексы ядра обычно называют просто мьютексами.

Мьютексы ядра – это диспетчерские объекты – эквиваленты спин-блокировок. Двумя важными отличиями мьютексов от спин-блокировок являются:

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

2. Для мьютексов предусмотрен механизм исключения взаимоблокировок (см. п. [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() должен следовать вызов одной из функций ожидания KeWait Xxx() (см. п. [11.2.1]). В этом случае гарантируется, что пара функций – освобождение мьютекса и ожидание – будет выполнена как одна операция, без возможного в противном случае переключения контекста потока. Возвращаемым значением будет 0, если мьютекс был освобожден, т.е. переведен из несигнального состояния в сигнальное. В противном случае возвращается ненулевое значение.

LONG KeReadStateMutex(IN PKMUTEX Mutex);

Возвращает состояние мьютекса – сигнальное или несигнальное.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: