KSPIN_LOCK 从 Driver 的主线程获取时阻塞

KSPIN_LOCK blocks when acquiring from Driver's main thread

我有一个 KSPIN_LOCK,它在 Windows 驱动程序的主线程和我用 PsCreateSystemThread 创建的一些线程之间共享。问题是如果我尝试获取自旋锁并且不解除阻塞,主线程会阻塞。我很困惑为什么会发生这种情况。这可能与主线程 运行 位于驱动程序 IRQL,而其他线程 运行 位于 PASSIVE_LEVEL 这一事实有关据我所知。

注意:如果我只 运行 主线程,acquiring/releasing 锁工作正常。

注意:我正在使用函数 KeAcquireSpinLockKeReleaseSpinLock 来 acquire/release 锁。

这是我的 "stuck" 自旋锁清单:

  1. 确保自旋锁已使用 KeInitializeSpinLock 初始化。如果 KSPIN_LOCK 持有未初始化的垃圾,那么第一次尝试获取它可能会永远旋转。
  2. 检查您是否没有获得它 recursively/nested。 KSPIN_LOCK不支持递归,如果你尝试它,它会永远旋转。
  3. 必须在 IRQL <= DISPATCH_LEVEL 获取正常自旋锁。如果您需要在 DIRQL 工作的东西,请查看 [1] and [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 就像一辆赛车,去掉了所有可选功能以最大限度地提高原始速度。如果您不计算微秒,您可能会合理地决定通过将低电平 KSPIN_LOCK 包裹在上面的东西中来重新添加加热座椅和 FM 收音机。 (借助#ifdefs 的魔力,如果需要,您可以随时将安全气囊从您的零售建筑中取出。)