ERESOURCE加锁函数

最常用的加锁函数只有两个:

  • ExAcquireResourceExclusiveLite:试图获取排它锁
  • ExAcquireResourceSharedLite:试图获取共享锁

除此之外,针对共享锁的获取,还有另外两个函数:

  • ExAcquireSharedStarveExclusive:可以早于等待中的排它请求者
  • ExAcquireSharedWaitForExclusive:不能早于排它的请求者

二者都是试图获取共享锁,和ExAcquireResourceSharedLite只是有些微的小差别。但这两个函数使用的并不多,只限于少见的特殊情况:

ERESOURCE本身是可以重入的,如果当前线程已经获得此锁的话,若再次试图获取,ExAcquireResourceSharedLite将会直接准予此请求,但ExAcquireSharedWaitForExclusive则不会,一定要等到此锁等待列表中所有的排它锁的请求者完成之后才会准予当前调用者。当然如果当前线程之前所获得的ERESOURCE锁是排它锁的话,将会直接准予通行,不然会导致死锁发生。

ExAcquireSharedStarveExclusiveExAcquireResourceSharedLite的差别只在一点:若已是共享锁状态,ExAcquireSharedStarveExclusive将忽略排它锁等待列表中的所有请求者,直接准予当前线程,以尽可能的减少当前线程的等待时间。

Continue reading » · Rating: · Written on: 02-25-16 · No Comments »

KEVENT,ERESOURCE及Thread Boost

在唤醒KEVENT时,函数KeSetEvent的第二个参数(KPRIORITY Increment)是用来临时提高被唤醒线程的调试优先级的,此举可以让睡眠在此KEVENT上的线程尽快得到处理以提高系统整体性能。

同样ERESOURCE也有类似的机制,在放锁时也会调高等待列表中处于饥饿状态的线程的优先级。

不过除此之外,ERESOURCE还有一种反向的Boost机制,即让等待者在试图获取锁时调高此锁当前拥有者线程的调度优先级,以加快属主线程的处理并尽可能早的释放锁资源。

反向操作却有可能遭遇这样一个问题:作为锁拥有者的线程未必是同步的,很有可能在获取ERESOURCE锁之后就因为异步处理事件(如Workitem,异步I/O请求)在不释放锁的情况下就退出(锁的释放将由异步事件来做),在此情况下ERESOURCE的属主线程指针就变成非法的了。为此Windows内核提供了一种机制来阻止问题的发生:线程等结构体一般都是至少8字节对齐的(最后3位均为0),通过将最后两位设为1的方式(|0x03)来甄别此指针数据是用户自己所指定的还是默认的系统线程,以此来避免访问已经释放的内存。

从Win7之后,FastFat及Ntfs都有对ERESOURCE的OwnerThread的处理,主要是异步的Noncached i/o事件的处理,其目的就是处理线程在获取ERESOURCE之后退出的情况。

Continue reading » · Rating: · Written on: 02-25-16 · No Comments »

FastMutex的陷阱

相对ERESOURCE来说,FastMutex的使用比较方便,不用考虑禁APC问题,也不用考虑FastMutex结构体的释放,用完即可以三不管。对于竞争不激烈且读写不明显的情况下,用FastMutex还是不错的。

不过使用过程中要注意其隐藏的陷阱:

1,ExAcquireFastMutex 会提升IRQL至APC_LEVEL,且保存原来的IRQL用以在ExReleaseFastMutex时恢复。所以在嵌套获取FastMutex时要特别注意放锁一定要依加锁顺序反向操作,不然会导致IRQL紊乱。为此系统特别提供了Unsafe操作函数(ExAcquireFastMutexUnsafe及ExReleaseFastMutexUnsafe),这两个函数不会操作IRQL,但使用上却有前提条件。

2,FastMutex的实现是通过原子锁与Gate对象来实现的,在没有竞争的情况下,通过原子锁可以速实现加锁及解锁,此外Gate对象是不可重入的,不像Event等(Dispatch Object)及ERESOURCE等可重入的锁(同一个线程可以同时加锁多次不会死锁)。

至于FastMutex为什么要提升IRQL至APC_LEVEL的原因,和文件系统调用FsRtlEnterFileSystem禁APC的原因是一样的,直接目的是防止当前线程被挂起(Suspend)。设想在没有禁止APC的情况下调用ExAcquireFastMutexUnsafe加锁之后线程随时可能被挂起,一旦被挂起的话系统中试图获取该锁的其它线程则只能等待,不管此线程是否高优先级或关键的系统任务。同理可以理解Event,ERESOURCE等为什么也需要禁止APC的原因。

除了FastMutex之外,Windows内核还实现了另外两种Mutex: Regular Mutex及Guarded Mutex。不过从Windows 8开始,GuardedMutext和FastMutex的实现是一样的,二者并无差别。Regular Mutex通过KeInitializeMutex初始化,加锁及放锁是通过KeWaitForXXXObject及KeReleaseMutex来操作的。我们知道 Event也是通过KeWaitForXXXObject来加锁的,但是Regular Mutex的加锁操作却会禁止当前线程的Kernel APC,Event却不会,所以在操作Event之前要显示地进入临界区或禁止APC操作以防止可能的死锁发生。

需要注意的是禁止Kernel APC并不会影响i/o操作,但是FastMutex及GauardedMutex不止是禁止Kernel APC,还会禁止Special APC,这样的话i/o操作是不允许的,因为i/o完成例程是在Special APC中做的。这些细微差别在使用中必须考虑到。

Continue reading » · Rating: · Written on: 02-17-16 · No Comments »