KSPIN_LOCK 从 Driver 的主线程获取时阻塞
KSPIN_LOCK blocks when acquiring from Driver's main thread
我有一个 KSPIN_LOCK
,它在 Windows 驱动程序的主线程和我用 PsCreateSystemThread
创建的一些线程之间共享。问题是如果我尝试获取自旋锁并且不解除阻塞,主线程会阻塞。我很困惑为什么会发生这种情况。这可能与主线程 运行 位于驱动程序 IRQL,而其他线程 运行 位于 PASSIVE_LEVEL 这一事实有关据我所知。
注意:如果我只 运行 主线程,acquiring/releasing 锁工作正常。
注意:我正在使用函数 KeAcquireSpinLock
和 KeReleaseSpinLock
来 acquire/release 锁。
这是我的 "stuck" 自旋锁清单:
- 确保自旋锁已使用 KeInitializeSpinLock 初始化。如果 KSPIN_LOCK 持有未初始化的垃圾,那么第一次尝试获取它可能会永远旋转。
- 检查您是否没有获得它 recursively/nested。 KSPIN_LOCK不支持递归,如果你尝试它,它会永远旋转。
- 必须在 IRQL <= DISPATCH_LEVEL 获取正常自旋锁。如果您需要在 DIRQL 工作的东西,请查看 [1] and [2].
- 检查是否有泄漏。如果一个处理器获得自旋锁,但忘记释放它,那么下一个处理器在尝试获得锁时将永远自旋。
- 确保没有内存安全问题。如果代码在自旋锁顶部随机写入一个非零值,这将导致它看起来已被获取,并且下一次获取将永远自旋。
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 的魔力,如果需要,您可以随时将安全气囊从您的零售建筑中取出。)
我有一个 KSPIN_LOCK
,它在 Windows 驱动程序的主线程和我用 PsCreateSystemThread
创建的一些线程之间共享。问题是如果我尝试获取自旋锁并且不解除阻塞,主线程会阻塞。我很困惑为什么会发生这种情况。这可能与主线程 运行 位于驱动程序 IRQL,而其他线程 运行 位于 PASSIVE_LEVEL 这一事实有关据我所知。
注意:如果我只 运行 主线程,acquiring/releasing 锁工作正常。
注意:我正在使用函数 KeAcquireSpinLock
和 KeReleaseSpinLock
来 acquire/release 锁。
这是我的 "stuck" 自旋锁清单:
- 确保自旋锁已使用 KeInitializeSpinLock 初始化。如果 KSPIN_LOCK 持有未初始化的垃圾,那么第一次尝试获取它可能会永远旋转。
- 检查您是否没有获得它 recursively/nested。 KSPIN_LOCK不支持递归,如果你尝试它,它会永远旋转。
- 必须在 IRQL <= DISPATCH_LEVEL 获取正常自旋锁。如果您需要在 DIRQL 工作的东西,请查看 [1] and [2].
- 检查是否有泄漏。如果一个处理器获得自旋锁,但忘记释放它,那么下一个处理器在尝试获得锁时将永远自旋。
- 确保没有内存安全问题。如果代码在自旋锁顶部随机写入一个非零值,这将导致它看起来已被获取,并且下一次获取将永远自旋。
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 的魔力,如果需要,您可以随时将安全气囊从您的零售建筑中取出。)