Использование обычных спин-блокировок

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).

Решение этой проблемы очень простое. Все блокировки, которые могут захватываться одновременно, помещаются в список в порядке убывания частоты использования. При необходимости захвата блокировок они должны быть захвачены в том порядке, в котором они указаны в списке. Таким образом, мы создали иерархию блокировок.