Widows下自旋锁的实现

来源:互联网 发布:ubuntu add to dash 编辑:程序博客网 时间:2024/05/01 15:20
In this part, we’ll discuss how the OS realize SpinLocks via IRQL

Realization of SpinLocks:

Spin locks are very commonly used in drivers to protect data that will be accessed by multiple driver routines running at varying IRQLs. But what’s it’s realizaton ?
Every book about “Operating System?will tell us that an atomic test-and-set instruction will be adopted. Is windows os uses this way ? The answer is NO. What windows nt adopts is IRQL. That is the spinlock routines will change the IRQL.

There are some kernel routines to acquire/release SpinLocks available for driver developers. These routines are not strange faces, so I’ll not introduce their functionalities here.

VOID
KeAcquireSpinLock(
IN PKSPIN_LOCK SpinLock,
OUT PKIRQL OldIrql
);

VOID
KeReleaseSpinLock(
IN PKSPIN_LOCK SpinLock,
IN KIRQL NewIrql
);

VOID
KeAcquireSpinLockAtDpcLevel(
IN PKSPIN_LOCK SpinLock
);

VOID
KeReleaseSpinLockFromDpcLevel(
IN PKSPIN_LOCK SpinLock
);

DDk says that “Callers of KeAcquireSpinLock must be running at IRQL <= DISPATCH_LEVEL? for dispatch_level routines, .it would be better to use KeAcuireSpinLockAtDpcLevel, and that callers of KeReleaseSpinLock are running at IRQL DISPATCH_LEVEL, but why ?

The following codes tell us the answer:

kd> u Hal!KeAcquireSpinLock
hal!KeAcquireSpinLock:
80066806 8b4c2404 mov ecx,[esp+0x4]
8006680a e849c7ffff call hal!KfAcquireSpinLock (80062f58)
8006680f 8b4c2408 mov ecx,[esp+0x8]
80066813 8801 mov [ecx],al
80066815 c20800 ret 0x8

hal!KfAcquireSpinLock:
80062f58 33c0 xor eax,eax

// Save current IRQL to al
80062f5a a024f0dfff mov al, [ffdff024]

// Change Current IRQL to Dispatch Level
80062f5f c60524f0dfff02 mov byte ptr [ffdff024],0x2
80062f66 c3 ret

Then we get the result: The acquiration of spin lock is just only improve current IRQL to DISPATCH_LEVEL. So the article “A Catalog of NT Synchronization Mechanisms?(refer. 2) says “Always relying on spin locks to protect access to shared data may be overkill? Because after the SpinLock is acquired , the current IRQL will be DISPATCH_LEVEL and then the NT dispatcher (scheduler) preemption will be disabled.

But for the routine which is already running at DISPATCH_LEVEL, they are advised .to use KeAcquireSpinLockAtDpcLevel instead. We can image what KeAcquireSpinLockAtDpcLevel do?

kd> u KeAcquireSpinLockAtDpcLevel
nt!KeAcquireSpinLockAtDpcLevel:
804022e4 c20400 ret 0x4

nt!KeReleaseSpinLockFromDpcLevel:
804022f4 c20400 ret 0x4

These two routines do nothing, and just return. As all DISPATCH_LEVEL all the routines will be executed synchronously, they can not interrupt each other, i.e. they are alreary synchronized.

When current IRQL > DISPATCH_LEVEL, we are warned never to call spin lock routines , or we’ll get BSOD. Here we get the reason: KeAcquireSpinLock will try to lower the current IRQL, which is not permitted by NT.