简介自旋锁的注意点和自旋锁实现原型代码
自旋锁是一种轻量级的多处理器间的同步机制(对单处理器无效) :roll:
它要求所占用的时间尽可能短(一般不超过25ms),因为此时别的处理器正在高速运转并等待锁的释放,所以不能长时间占有
与其他相比,采用自旋锁的好处是:不会被中断或调度打断(它运行在DISPATCH_LEVEL上,不会被软中断)
自旋锁注意点:
1.被自旋锁保护的临界区代码执行时不能睡眠。单核处理器下,获取到锁的线程睡眠,若恰好此时CPU调度的另一个执行线程也需要获取这个锁,则会造成死锁;多核处理器下,若想获取锁的线程在同一个处理器下,同样会造成死锁,若位于另外的处理器,则会长时间占用CPU等待睡眠的线程释放锁,从而浪费CPU资源
2. 一定不要使自旋锁 (Spin Lock) 保持锁定状态的时间超过您的需要。 要使系统获得更好的总体性能,请不要使任何系统范围内有效的自旋锁的锁定时间超过 25 微秒
3.一定不要通过调用 KeReleaseSpinLockFromDpcLevel 来释放 KeAcquireSpinLock 所获取的自旋锁,因为这会使原始 IRQL 无法被还原
4.自旋锁锁定时,一定不要调用 IoStartNextPacket。 这将使系统死锁
5.自旋锁锁定时,一定不要调用 IoCompleteRequest。 这将使系统死锁
6.一定不要在取消例程中调用 IoAcquireCancelSpinLock,因为该例程被调用时已经获取了系统级的取消自旋锁
7.在自旋锁锁定时,一定不要调用驱动程序以外的代码,因为这会引起死锁
简单贴下WRK下自旋锁的实现代码:1.初始化
FORCEINLINE
VOID
NTAPI
KeInitializeSpinLock (
__out PKSPIN_LOCK SpinLock
)
{
*SpinLock = 0;
}
KSPIN_LOCK
SpinLock实际是一个操作系统相关的无符号整数,初始化,置为0,有信号状态
2.获取
//宏定义
#define KeAcquireSpinLock(SpinLock, OldIrql) \
*(OldIrql) = KeAcquireSpinLockRaiseToDpc(SpinLock)
-->
KIRQL KeAcquireSpinLockRaiseToDpc (__inout PKSPIN_LOCK SpinLock)
{
KIRQL OldIrql;
//提IRQL到DISPATCH_LEVEL,这样不会发生线程切换(线程调度也是在DISPATCH_LEVEL)
//单核下,由于线程不会被切换,自然就达到了互斥效果
OldIrql = KfRaiseIrql(DISPATCH_LEVEL);
KxAcquireSpinLock(SpinLock);
return OldIrql; //返回原始的IRQL
}
-->
VOID KxAcquireSpinLock (__inout PKSPIN_LOCK SpinLock)
{
if (InterlockedBitTestAndSet64((LONG64 *)SpinLock, 0))
{
KxWaitForSpinLockAndAcquire(SpinLock);
}
return;
}
-->InterlockedBitTestAndSet
BOOLEAN InterlockedBitTestAndSet (IN LONG *Base,IN LONG Bit)
{
__asm {
mov eax, Bit
mov ecx, Base
//lock指令是一种前缀,它可与其他指令联合,用来维持总线的锁存信号直到与其联合的指令执行完为止。当CPU与其他CPU协同工作时,该指令可避免破坏有用信息。
lock bts [ecx], eax//eax传入的是0,所以bts是把*SpinLock的第0位设置为1
setc al
};
}
-->
ULONG64 KxWaitForSpinLockAndAcquire (__inout PKSPIN_LOCK SpinLock)
{
ULONG64 SpinCount = 0;
// Wait for spin lock to become free.循环等待,直到SpinLock为0
do {
do {
KeYieldProcessor();
} while (*(volatile LONG64 *)SpinLock != 0);
} while(InterlockedBitTestAndSet64((LONG64 *)SpinLock, 0));
return SpinCount;
}
3. 释放
VOID KeReleaseSpinLock (__inout PKSPIN_LOCK SpinLock,
__in KIRQL OldIrql)
{
KxReleaseSpinLock(SpinLock);
KeLowerIrql(OldIrql);// 恢复IRQL
return;
}
-->
VOID KxReleaseSpinLock (__inout PKSPIN_LOCK SpinLock)
{
InterlockedAnd64((LONG64 *)SpinLock, 0);//释放时进行与操作设置其为0
return;
}