您的位置:首页 > 编程语言

0.driverbase-自旋锁(SpinLock)wrk代码分析

2014-05-04 19:18 260 查看
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;
}


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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: