Программирование драйверов Windows

       

Семафоры


Семафоры &#8212 это объекты синхронизации, имеющие внутренний счетчик обращений и пребывающие в сигнальном состоянии тогда, когда значение этого счетчика больше нуля. Состояние становится несигнальным сразу же, как только счетчик принимает нулевое значение. Другими словами, семафор &#8212 это не что иное, как счетный мьютекс (см. ниже).

Для увеличения на единицу значения внутреннего счетчика семафора следует выполнить вызов KeReleaseSemaphore. Предположим, два потока t1 и t2 ожидают (используя вызов KeWaitForSingleObject) сигнального состояния семафора S, счетчик которого в настоящий момент равен 0. Когда третий поток t0 выполнит вызов KeReleaseSemaphore, значение счетчика возрастет до 1. Следовательно, одному из ожидающих потоков будет разрешено продолжить работу. Вызов KeWaitForSingleObject вернется из состояния ожидания, уменьшив счетчик семафора на единицу. Соответственно, второй поток останется заблокированным. Какому конкретно потоку повезет, почти что неизвестно &#8212 в том смысле, что не следует строить на этом расчет. Если это имеет большое значение, то следует усложнить схему синхронизации.

Удобно использовать семафоры для "охраны" созданных драйвером очередей или списков объектов. При добавлении объекта в очередь (например, собственную очередь IRP пакетов) производится увеличение на единицу счетчика семафора. Как только некий рабочий поток удаляет объект из очереди или списка, он уменьшает значение семафора. Когда счетчик семафора станет равным нулю, а очередь опустеет, поток (или потоки) перейдет в состояние ожидания.

Таблица 10.31. Функции для работы с объектами семафоров



Что необходимо сделать Используемый вызов
Создать семафор KeInitializeSemaphore
Увеличить счетчик семафора KeReleaseSemaphore
Запросить состояние KeReadStateSemaphore
Уменьшить счетчик семафора KeWaitForSingleObject

KeWaitForMultipleObject

Для инициализации семафора используется вызов, KeInitializeSemaphore, которому необходимо передать не только два параметра будущего семафора (см.
таблицу 10.32), но и область памяти под будущий объект, выделенную, например, вызовом ExAllocatePool.

Таблица 10.32. Прототип вызова KeInitializeSemaphore

VOID KeInitializeSemaphore IRQL == PASSIVE_LEVEL
Параметры Инициализирует объект семафора и устанавливает текущее значение его счетчика и предельное значение, которого этот счетчик может достигать
IN PKSEMAPHORE pSemaphore Указатель на область, подготовленную для объекта семафора
IN LONG CountValue Текущее (начальное) значение счетчика
IN LONG CountLimit Предел для значений счетчика (должно быть положительным)
Возвращаемое значение void
Вызов KeReadStateSemaphore получает указатель на объект семафора и возвращает значение типа LONG, равное текущему значению счетчика семафора. Соответственно, нулевое возвращенное значение указывает на то, что семафор пребывает в несигнальном состоянии. Вызов KeReadStateSemaphore может выполняться на любом уровне IRQL, что однозначно указывает на то, какие правила применяет система к объекту семафора: он непременно должен размещаться в области нестраничной памяти.

Параметры вызова KeReleaseSemaphore описаны в таблице 10.33.

Таблица 10.33. Прототип вызова KeReleaseSemaphore

VOID KeReleaseSemaphore IRQL == PASSIVE_LEVEL
Параметры Инициализирует объект семафора и устанавливает текущее значение его счетчика и предельное значение, которого этот счетчик может достигать
IN PKSEMAPHORE pSemaphore Указатель на область, подготовленную для объекта семафора
IN LONG CountValue Текущее (начальное) значение счетчика
IN LONG CountLimit Предел для значений счетчика (должно быть положительным)
Возвращаемое значение void

Содержание раздела