Использование обычных спин-блокировок
VOID KeInitializeSpinLock(IN PKSPIN_LOCK SpinLock);
Инициализирует объект ядра KSPIN_LOCK. Память под спин-блокировку уже должна быть выделена в невыгружаемой памяти.
VOID KeAcquireSpinLock(IN PKSPIN_LOCK SpinLock, OUT PKIRQL OldIrql);
Захват спин-блокировки. Функция не вернет управление до успеха захвата блокировки. При завершении функции уровень IRQL повышается до уровня DISPATCH_LEVEL. Во втором параметре возвращается уровень IRQL, который был до захвата блокировки (он должен быть <= DISPATCH_LEVEL).
VOID KeReleaseSpinLock(IN PKSPIN_LOCK SpinLock, OUT PKIRQL NewIrql);
Освобождение спин-блокировки и установка уровня IRQL в значение параметра NewIrql. Это должно быть то значение, которое вернула функция KeAcquireSpinLock() в параметре OldIrql.
VOID KeAcquireLockAtDpcLevel(IN PKSPIN_LOCK SpinLock);
Это оптимизированная функция захвата спин-блокировки кодом, уже работающем на уровне IRQL DISPATCH_LEVEL. В этом случае изменение уровня IRQL не требуется. На однопроцессорной платформе эта функция вообще ничего не делает, т.к. синхронизация обеспечивается самой архитектурой IRQL.
VOID KeReleaseLockFromDpcLevel(IN PKSPIN_LOCK SpinLock);
Это функция освобождения спин-блокировки кодом, захватившим блокировку с помощью функции KeAcquireLockAtDpcLevel(). На однопроцессорной платформе эта функция ничего не делает.
Пример использования обычных спин-блокировок
typedef struct _DEVICE_EXTENSION
{
...
KSPIN_LOCK spinlock
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
NTSTATUS DriverEntry(....)
{
KeInitializeSpinLock(&extension->spinlock);
}
NTSTATUS DispatchReadWrite( .... )
{
KIRQL OldIrql;
...
KeAcquireSpinLock(&extension->spinlock, &0ldIrql);
// произвести обработку данных, защищенных спин-блокировкой
KeReleaseSpinLock(&extension->spinlock, OldIrql);
}
[11.1.1.2] Проблема взаимоблокировок (deadlocks)
Если поток попробует захватить спин-блокировку повторно, он войдет в бесконечный цикл ожидания – “повиснет”. Такая же ситуация возникнет, если два потока используют две спин-блокировки. Поток 1 захватывает блокировку 1, одновременно с этим поток 2 захватывает блокировку 2. Затем поток 1 пробует захватить блокировку 2, а поток 2 – блокировку 1. Оба потока “виснут”. Эту ситуацию можно распространить на произвольное число потоков, она широко известна и носит название взаимоблокировки (deadlocks).
Решение этой проблемы очень простое. Все блокировки, которые могут захватываться одновременно, помещаются в список в порядке убывания частоты использования. При необходимости захвата блокировок они должны быть захвачены в том порядке, в котором они указаны в списке. Таким образом, мы создали иерархию блокировок.