KSPIN_LOCK блокирует при получении из основного потока водителя - PullRequest
1 голос
/ 04 апреля 2019

У меня есть KSPIN_LOCK, общий для основного потока драйвера Windows и некоторых потоков, созданных с помощью PsCreateSystemThread.Проблема в том, что основной поток блокируется, если я пытаюсь получить спин-блокировку, и не разблокируется.Я очень смущен тем, почему это происходит ... это, вероятно, как-то связано с тем, что основной поток работает на драйвере IRQL, в то время как другие потоки работают на PASSIVE_LEVEL, насколько я знаю.

ПРИМЕЧАНИЕ. ЕслиЯ запускаю только основной поток, получение / снятие блокировки работает нормально.

ПРИМЕЧАНИЕ. Я использую функции KeAcquireSpinLock и KeReleaseSpinLock для получения / снятия блокировки.

1 Ответ

1 голос
/ 04 апреля 2019

Вот мой контрольный список для «застрявшего» спинлока:

  1. Убедитесь, что спин-блокировка была инициализирована с помощью KeInitializeSpinLock. Если KSPIN_LOCK содержит неинициализированный мусор, то первая попытка его получения будет вращаться вечно.
  2. Убедитесь, что вы не получаете его рекурсивно / вложенно. KSPIN_LOCK не поддерживает рекурсию, и если вы попробуете ее, она будет вращаться вечно.
  3. Обычные спин-блокировки должны быть получены в IRQL <= DISPATCH_LEVEL. Если вам нужно что-то, что работает в DIRQL, посмотрите <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/wdf/synchronizing-interrupt-code" rel="nofollow noreferrer"> [1] и [2] .
  4. Проверка на утечки. Если один процессор получает спин-блокировку, но забывает освободить ее, то следующий процессор будет вращаться вечно при попытке получить блокировку.
  5. Убедитесь, что нет проблем с безопасностью памяти. Если код случайным образом записывает ненулевое значение поверх спин-блокировки, это приведет к тому, что оно будет получено, а следующее получение будет вращаться вечно.

Некоторые из этих проблем могут быть легко и автоматически обнаружены с помощью Driver Verifier; используйте его, если вы его еще не используете. Другие проблемы могут быть обнаружены, если вы включите спин-блокировку в маленький помощник, который добавляет ваши собственные утверждения. Например:

typedef struct _MY_LOCK {
    KSPIN_LOCK Lock;
    ULONG OwningProcessor;
    KIRQL OldIrql;
} MY_LOCK;

void MyInitialize(MY_LOCK *lock) {
    KeInitializeSpinLock(&lock->Lock);
    lock->OwningProcessor = (ULONG)-1;
}

void MyAcquire(MY_LOCK *lock) {
    ULONG current = KeGetCurrentProcessorIndex();
    NT_ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
    NT_ASSERT(current != lock->OwningProcessor); // check for recursion
    KeAcquireSpinLock(&lock->Lock, &lock->OldIrql);
    NT_ASSERT(lock->OwningProcessor == (ULONG)-1); // check lock was inited
    lock->OwningProcessor = current;
}

void MyRelease(MY_LOCK *lock) {
    NT_ASSERT(KeGetCurrentProcessorIndex() == lock->OwningProcessor);
    lock->OwningProcessor = (ULONG)-1;
    KeReleaseSpinLock(&lock->Lock, lock->OldIrql);
}

Обертки вокруг KSPIN_LOCK распространены. KSPIN_LOCK походит на гоночный автомобиль, у которого удалены все дополнительные функции, чтобы максимизировать сырую скорость. Если вы не учитываете микросекунды, вы можете разумно добавить обратно подогрев сидений и FM-радио, завернув низкоуровневый KSPIN_LOCK во что-то подобное вышеописанному. (И с помощью магии #ifdefs вы всегда можете убрать подушки безопасности из ваших розничных сборок, если вам нужно.)

...