Windows 驱动开发中 ExAcquireResourceExclusiveLite 和其他锁的区别:
1 Windows 驱动开发中 ExAcquireResourceExclusiveLite 和其他锁的区别
1. ExAcquireResourceExclusiveLite(独占资源锁)
特点:
- 是一种读写锁(ERESOURCE)的独占模式
- 同一时间只允许一个线程获得锁
- 可以递归获取(同一线程可以多次获取)
- 支持等待和超时机制
- 适用于内核模式代码
- 可以与共享锁(ExAcquireResourceSharedLite)配合使用
2. ExAcquireResourceSharedLite(共享资源锁)
特点:
- 同一时间允许多个线程以共享方式访问资源
- 与独占锁互斥
- 也支持递归获取
- 适合读多写少的场景
3. KeAcquireSpinLock(自旋锁)
特点:
- 轻量级锁,适合短时间持有
- 不会引起线程调度
- 占用CPU进行自旋等待
- 不能递归获取
- IRQL 限制较严格
4. FastMutex(快速互斥体)
特点:
- 比自旋锁更适合长时间持有
- 会引起线程调度(阻塞等待)
- 不能递归获取
- IRQL 限制相对宽松
5. KeWaitForMutexObject(互斥体)
特点:
- 可以在线程间同步
- 支持递归获取
- 会引起线程调度
- 可以跨进程使用
使用场景比较:
1. ExAcquireResourceExclusiveLite 适用于:
- 需要读写保护的复杂数据结构
- 可能长时间持有锁的场景
- 需要读写锁语义的场景
- 需要递归获取锁的场景
2. 自旋锁适用于:
- 极短时间的同步
- 不允许休眠的高 IRQL 环境
- 简单的原子操作保护
3. FastMutex 适用于:
- 中等时间的同步
- 不需要递归获取
- 允许线程调度的场景
4. 互斥体适用于:
- 长时间的同步
- 需要跨进程同步
- 需要递归获取
- 允许线程调度的场景
代码示例:
```c
// 资源锁使用示例
ERESOURCE Resource;
ExInitializeResourceLite(&Resource);
// 独占方式获取
ExAcquireResourceExclusiveLite(&Resource, TRUE);
// 临界区代码
ExReleaseResourceLite(&Resource);
// 共享方式获取
ExAcquireResourceSharedLite(&Resource, TRUE);
// 临界区代码
ExReleaseResourceLite(&Resource);
// 自旋锁使用示例
KSPIN_LOCK SpinLock;
KIRQL OldIrql;
KeInitializeSpinLock(&SpinLock);
KeAcquireSpinLock(&SpinLock, &OldIrql);
// 临界区代码
KeReleaseSpinLock(&SpinLock, OldIrql);
// FastMutex 使用示例
FAST_MUTEX FastMutex;
ExInitializeFastMutex(&FastMutex);
ExAcquireFastMutex(&FastMutex);
// 临界区代码
ExReleaseFastMutex(&FastMutex);
```
性能考虑:
1. 资源锁开销较大,但功能最完整
2. 自旋锁开销最小,但功能有限
3. FastMutex 介于两者之间
4. 互斥体开销较大,但跨进程同步能力强
选择建议:
1. 如果需要读写锁语义,选择 ERESOURCE
2. 如果是短时间同步,选择自旋锁
3. 如果是中等时间同步且不需要递归,选择 FastMutex
4. 如果需要跨进程同步,选择互斥体
2 具体使用场合和 IRQL 限制:
1. ExAcquireResourceExclusiveLite (ERESOURCE)
IRQL 限制:
- 必须 < DISPATCH_LEVEL (最高 APC_LEVEL)
- 获取锁时可能会休眠
使用场合:
```c
// 适用于复杂数据结构的保护
typedef struct _DEVICE_EXTENSION {
ERESOURCE DeviceResource;
LIST_ENTRY PendingIoQueue;
// ... 其他成员
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
// 初始化
ExInitializeResourceLite(&deviceExtension->DeviceResource);
// 在 IRP 处理中使用
NTSTATUS HandleIoRequest(PDEVICE_EXTENSION DevExt)
{
// 可以在 PASSIVE_LEVEL 使用
ExAcquireResourceExclusiveLite(&DevExt->DeviceResource, TRUE);
InsertTailList(&DevExt->PendingIoQueue, &newRequest->ListEntry);
ExReleaseResourceLite(&DevExt->DeviceResource);
return STATUS_SUCCESS;
}
```
2. KeSpinLock
IRQL 限制:
- 可在任何 IRQL 级别获取
- 获取后提升到 DISPATCH_LEVEL
- 持有期间不能调用可能休眠的函数
使用场合:
```c
// 适用于中断处理和短期同步
typedef struct _DEVICE_CONTEXT {
KSPIN_LOCK SpinLock;
ULONG Counter;
// ... 其他需要快速访问的数据
} DEVICE_CONTEXT, *PDEVICE_CONTEXT;
VOID InterruptHandler(PDEVICE_CONTEXT Context)
{
KIRQL oldIrql;
// 可以在高 IRQL 使用
KeAcquireSpinLock(&Context->SpinLock, &oldIrql);
Context->Counter++;
// 快速操作,不要有复杂处理
KeReleaseSpinLock(&Context->SpinLock, oldIrql);
}
```
3. FastMutex
IRQL 限制:
- 必须 <= APC_LEVEL
- 获取时会禁用 APC
- 不能在中断处理中使用
使用场合:
```c
// 适用于中等复杂度的数据保护
typedef struct _QUEUE_MANAGER {
FAST_MUTEX QueueLock;
LIST_ENTRY Queue;
ULONG QueueSize;
} QUEUE_MANAGER, *PQUEUE_MANAGER;
VOID ProcessQueue(PQUEUE_MANAGER QueueMgr)
{
// 在普通驱动例程中使用
ExAcquireFastMutex(&QueueMgr->QueueLock);
if (!IsListEmpty(&QueueMgr->Queue)) {
// 处理队列
QueueMgr->QueueSize--;
}
ExReleaseFastMutex(&QueueMgr->QueueLock);
}
```
4. Mutex Object (KeWaitForMutexObject)
IRQL 限制:
- 必须 = PASSIVE_LEVEL
- 可能导致线程休眠
使用场合:
```c
// 适用于需要长时间同步的场景
VOID LongRunningOperation(PDEVICE_EXTENSION DevExt)
{
// 只能在 PASSIVE_LEVEL 使用
KeWaitForMutexObject(
&DevExt->Mutex,
Executive,
KernelMode,
FALSE,
NULL
);
// 可以执行长时间操作
// 可以调用可能休眠的函数
KeReleaseMutex(&DevExt->Mutex, FALSE);
}
```
具体场景选择指南:
1. 中断处理程序 (ISR):
```c
// 只能使用自旋锁
VOID ISR(...)
{
KIRQL oldIrql;
KeAcquireSpinLock(&SpinLock, &oldIrql);
// 快速处理
KeReleaseSpinLock(&SpinLock, oldIrql);
}
```
2. DPC 例程:
```c
// 可以使用自旋锁
VOID DpcRoutine(...)
{
// 已经在 DISPATCH_LEVEL
KeAcquireSpinLockAtDpcLevel(&SpinLock);
// 处理
KeReleaseSpinLockFromDpcLevel(&SpinLock);
}
```
3. 普通驱动例程:
```c
// 可以使用所有类型的锁
NTSTATUS DriverRoutine(...)
{
// 根据需求选择合适的锁
if (简单快速操作) {
// 使用自旋锁
KeAcquireSpinLock(...);
} else if (中等复杂度操作) {
// 使用 FastMutex
ExAcquireFastMutex(...);
} else if (复杂操作需要读写锁) {
// 使用 ERESOURCE
ExAcquireResourceExclusiveLite(...);
}
}
```
IRQL 限制总结:
1. ERESOURCE:
- 获取:< DISPATCH_LEVEL
- 释放:= 获取时的 IRQL
2. SpinLock:
- 获取:任何 IRQL
- 持有:DISPATCH_LEVEL
- 释放:恢复到之前的 IRQL
3. FastMutex:
- 获取:<= APC_LEVEL
- 持有:APC_LEVEL
- 释放:恢复到之前的 IRQL
4. Mutex Object:
- 获取:PASSIVE_LEVEL
- 持有:PASSIVE_LEVEL
- 释放:PASSIVE_LEVEL
选择建议:
1. 如果在高 IRQL(如中断):只能用自旋锁
2. 如果操作快速且简单:优先考虑自旋锁
3. 如果需要休眠或长时间操作:使用 ERESOURCE 或 Mutex Object
4. 如果需要读写锁语义:使用 ERESOURCE
5. 如果是中等复杂度操作:考虑 FastMutex