http://hgy413.com/1335.html
简介自旋锁的注意点和自旋锁实现原型代码
自旋锁是一种轻量级的多处理器间的同步机制(对单处理器无效) :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; }
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; }