KSPIN_LOCK
ERESOURCE
FAST_MUTEX
同步(A告诉B发生了什么事)
KEVENT
KSEMAPHORE
KMUTEX
KeWaitForSingleObject
KEVENT
KMUTEX/KMUTANT
KPROCESS
KQUEUE
KSEMAPHORE
KTHREAD
KTIMER(都带dispatcher header)
fileobject/driverobject等不行
例子:
LARGE_INTEGER TimeOut = {0};
TimeOut.QuadPart = -10 * 10000000i64;
KeWaitForSingleObject( &kSemaphore,
Executive,
KernelMode,
FALSE,
&TimeOut//0,不等;NULL,无限等待
);
1.KEVENT
用于线程同步
Event两个状态:
Signaled
Non-signaled
Event两个类别:
Notification事件:不自动恢复
synchronization事件:自动恢复
创建:IoCreateNotificationEvent
KEVENT waitEvent;
KeInitializeEvent(&waitEvent,
NotificationEvent,
FALSE );
//开灯
KeSetEvent(&waitEvent,
IO_NO_INCREMENT,
FALSE );
//等灯
KeWaitForSingleObject(
&waitEvent,
Executive,
KernelMode,
FALSE,
NULL );//0,立即返回;
//NULL无限等待
//关灯
KeClearEvent(&waitEvent);
KeResetEvent(&waitEvent);
进程创建监视:R0到R3通信
while (1)
{
//发送DeviceIoControl下去拿数据
Sleep(1000);
}
缺点:
效率问题
Event创建:
L\\BaseNamedObjects\\ProcEvent
IoCreateNotificationEvent
数据存放:
定义
typedef struct _DEVICE_EXTENSION
{
HANDLE hProcessHandle; // 事件对象句柄
PKEVENT ProcessEvent; // 用户和内核通信的事件对象指针
HANDLE hParentId; // 在回调函数中保存进程信息
HANDLE hProcessId;
BOOLEAN bCreate;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
IoCreateDevice( DriverObject, sizeof(DEVICE_EXTENSION), &ustrDeviceName,...);
访问:
// 获得DEVICE_EXTENSION结构
PDEVICE_EXTENSION deviceExtension =
(PDEVICE_EXTENSION)g_pDeviceObject->DeviceExtension;
// 保存信息
deviceExtension->hParentId = hParentId;
deviceExtension->hProcessId = PId;
deviceExtension->bCreate = bCreate;
事件触发
// 触发事件,通知应用程序
KeSetEvent(deviceExtension->ProcessEvent, 0, FALSE);
KeClearEvent(deviceExtension->ProcessEvent);
#define EVENT_NAME L"\\Global\\ProcEvent"
// 打开内核事件对象
HANDLE hProcessEvent = ::OpenEventW(SYNCHRONIZE, FALSE, EVENT_NAME);
while (::WaitForSingleObject(hProcessEvent, INFINITE))
{
//bugs to fix here
//发送DeviceIoControl下去拿数据
}
KSPIN_LOCK
用于线程互斥
使用注意事项:
多CPU共享安全
提升IRQL到DPC
禁止访问分页内存
获得时间越短越好
使用方法:
//定义
KIRQL OldIrql;
KSPIN_LOCK mySpinLockProc;
//获得
KeAcquireSpinLock(
&mySpinLockProc,
&OldIrql);
//对数据进行操作和访问
……
//释放
KeReleaseSpinLock(
&mySpinLockProc,
OldIrql);
1) 忙等,不会阻塞系统。对忙等线程的调度则该线程会占着 CPU不放,一直轮循,因此Spinlock适用于等待时间不会太长(例如不要超过25微秒)的情况。而Mutex不是,它会系统阻塞请求线程。如果你需要长时间串行化访问一个对象,应该首先考虑使用互斥量而不是自旋锁。
2) Spinlock请求成功之后,CPU的执行级别会提升到DL,Mutex不会。
3) DL及以下级别都可以请求Spinlock。Mutex通常在PL请求,在DL上TIMEOUT需设为0。
4) Spinlock是“非递归锁”,即不能递归获得该锁,而Mutex是“递归锁”。
5) Spinlock主要用于多CPU。但效率不高,使用ERESOURCE较好
ERESOURCE(读写共享锁)
typedef struct _MY_LOCK
{
ERESOURCE m_Lock;//用于互斥
}MY_LOCK;
VOID __stdcall LockWrite(MY_LOCK* lpLock)
{
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&lpLock->m_Lock, TRUE);
}
VOID __stdcall UnLockWrite(MY_LOCK* lpLock)
{
ExReleaseResourceLite(&lpLock->m_Lock);
KeLeaveCriticalRegion();
}
VOID __stdcall LockRead(MY_LOCK* lpLock)
{
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&lpLock->m_Lock, TRUE);
}
VOID __stdcall LockReadStarveWriter(MY_LOCK* lpLock)
{
KeEnterCriticalRegion();
//读优先
ExAcquireSharedStarveExclusive(&lpLock->m_Lock, TRUE);
//写优先
//ExAcquireSharedWaitForExclusive
}
VOID __stdcall UnLockRead(MY_LOCK* lpLock)
{
ExReleaseResourceLite(&lpLock->m_Lock);
KeLeaveCriticalRegion();
}
VOID __stdcall InitLock(MY_LOCK* lpLock)
{
ExInitializeResourceLite(&lpLock->m_Lock);
}
VOID __stdcall DeleteLock(MY_LOCK* lpLock)
{
ExDeleteResourceLite(&lpLock->m_Lock);
}
ERESOURCE的使用
LIST_ENTRY g_WaitList;
MY_LOCK g_WaitListLock;
//DriverEntry里
InitLock(&g_WaitListLock);
//多线程环境里
LockWrite(&g_WaitListLock);
lpWaitEntry = LookupWaitEntryByID(&g_WaitList, lpReply->m_ulWaitID);
if (lpWaitEntry != NULL)
{
lpWaitEntry->m_bBlocked = lpReply->m_ulBlocked;
KeSetEvent(&lpWaitEntry->m_ulWaitEvent, 0, FALSE);
return;
}
UnLockWrite(&g_WaitListLock);
//DriverUnload里
DeleteLock(&g_WaitListLock);
FAST_MUTEX
用于互斥
APC_LEVEL
cannot be acquired recursively
FAST_MUTEX gSfilterAttachLock
ExInitializeFastMutex( &gSfilterAttachLock );
ExAcquireFastMutex( &gSfilterAttachLock );
ExAcquireFastMutex( &gSfilterAttachLock );//错
//Do something here
……
ExReleaseFastMutex( &gSfilterAttachLock );
ExReleaseFastMutex( &gSfilterAttachLock );
KSEMAPHORE
用于同步与多个资源共享访问
KSEMAPHORE kSemaphore;
KeInitializeSemaphore(
&kSemaphore,
1,//信号量的初始值
2 //信号量的最大值
);
LARGE_INTEGER waitTime = {0};
waitTime.QuadPart = -1*10000000i64;
KeWaitForSingleObject(&kSemaphore, Executive, KernelMode, FALSE, &waitTime);//0,立即返回;NULL,无限等待
KeReleaseSemaphore(&kSemaphore ,IO_NO_INCREMENT , 1 , FALSE );
KMUTEX
基本淘汰,使用FAST_MUTEX取代
特点:
FAST_MUTEX无法递归,KMUTEX可以
FAST_MUTEX无法WAIT,KMUTEX可以
FAST_MUTEX 在APC LEVE,KMUTEX任意LEVEL
用法:
KMUTEX mutex;
KeInitializeMutex(
& mutex,
0
);
KeWaitForSingleObject(& mutex, Executive, KernelMode, FALSE, &MmOneSecond);
KeReleaseMutex(& mutex, FALSE);
R0同步互斥总结
DISPATCH_LEVEL:
SpinLock
APC/PASSIVE:
互斥:ERESOURCE/FAST_MUTEX
同步:KEVENT/KSEMAPHORE
R3/R0同步通信:
KEVENT
整数增减赋值:
InterlockedExchange
InterlockedIncrement
R3多线程同步总结
Critical Section/Mutex/Semaphore/Event
1. Critical Section与Mutex作用非常相似,但Mutex是可以命名的,也就是说它可以跨越进程使用。所以创建互斥量需要的资源更多,如果只为了在进程内部使用的话,使用临界区会带来速度上的优势并能够减少资源占用量。因为互斥量是跨进程的,互斥量一旦被创建,就可以通过名字打开它。
2.互斥量(Mutex),信号量(Semaphore),事件(Event)都可以跨越进程来进行同步数据操作(一个进程创建之后,另外的进程可以通过名字打开它,从而用于进程间的数据同步)
3.通过Mutex可以指定资源被独占的方式使用,但如果一个资源允许N(N>1)个进程或者线程访问,这时候如果利用Mutex就没有办法完成这个要求, Semaphore可以,是一种资源计数器。
Critical_section
struct RTL_CRITICAL_SECTION
{
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread;
HANDLE LockSemaphore;
ULONG_PTR SpinCount;
};
DebugInfo 此字段包含一个指针,指向系统分配的伴随结构,该结构的类型为
RTL_CRITICAL_SECTION_DEBUG
LockCount 这是临界区中最重要的一个字段。它被初始化为数值 -1;此数值等于或大于 0 时,表示此临界区被占用。当其不等于 -1 时,OwningThread 字段包含了拥有此临界区的线程 ID。此字段与 (RecursionCount -1) 数值之间的差值表示有多少个其他线程在等待获得该临界区。
RecursionCount 此字段包含所有者线程已经获得该临界区的次数。如果该数值为零,下一个尝试获取该临界区的线程将会成功。
OwningThread 此字段包含当前占用此临界区的线程的线程标识符。此线程 ID 与 GetCurrentThreadId 之类的 API 所返回的 ID 相同。
LockSemaphore 它实际上是一个自复位事件,而不是一个信号。它是一个内核对象句柄,用于通知操作系统:该临界区现在空闲。操作系统在一个线程第一次尝试获得该临界区,但被另一个已经拥有该临界区的线程所阻止时,自动创建这样一个句柄。应当调用 DeleteCriticalSection(它将发出一个调用该事件的 CloseHandle 调用,并在必要时释放该调试结构),否则将会发生资源泄漏。
SpinCount 仅用于多处理器系统。在多处理器系统中,如果该临界区不可用,调用线程将在对与该临界区相关的信号执行等待操作之前,旋转 dwSpinCount 次。如果该临界区在旋转操作期间变为可用,该调用线程就避免了等待操作。旋转计数可以在多处理器计算机上提供更佳性能,其原因在于在一个循环中旋转通常要快于进入内核模式等待状态。此字段默认值为零,但可以用InitializeCriticalSectionAndSpinCount API 将其设置为一个不同值。
CAutoLocker
class CLock
{
public:
void Lock() {EnterCriticalSection(&m_sec);}
void Unlock() {LeaveCriticalSection(&m_sec);}
CLock () {InitializeCriticalSection(&m_sec);}
~ CLock () {DeleteCriticalSection(&m_sec);}
private:
CRITICAL_SECTION m_sec;
};
class CAutoLock
{
public:
CAutoLock(CLock * lpLock) :
m_pLock (lpLock)
{
m_pLock ->Lock();
}
~CAutoLock()
{
m_pLock ->Unlock();
}
private:
CLock * m_pLock;
};
使用例子:
{
CLock m_lock;
CAutoLock(&m_lock);
….
}
写一个驱动中的多线程安全程序
ULONG g_ulTotal = 0;
FAST_MUTEX g_fmLock;
ExAcquireFastMutex(&g_fmLock);
g_ulTotal++;
ExReleaseFastMutex(&g_fmLock);