内核模式下的等待
在内核模式下,有两个函数负责等待内核同步对象。 分别是KeWaitForSingleObject和KeWaitForMultipleObject函数。
NTSTATUS
KeWaitForSingleObject (
PVOID Object, //同步内核对象的指针
KWAIT_REASON WaitReason, //内核模式下设置此值为Executive
KPROCESSOR_MODE WaitMode, //驱动中应该设置为KernelMode
BOOLEAN Alertable, //是否是课唤醒的。一般设置为FALSE
PLARGE_INTEGER Timeout //已经转换为NT时间的格式,负数表示相对于现在的时间单位是-100nm
);
内核模式下开始多线程
一般在多线程中才需要同步处理机制,这里介绍一下如何在内核模式下创建新线程。
内核函数PsCreateSystemThread负责创建新线程。该函数可以创建两种线程,一 种是用户
线程,一种是系统线程。用户线程是属于当前进程中的线程。当前进程指的是当前I/O操
作的发起者。如果在IRP_MJ_READ的派遣函数中调用PsCreateSystemThread 函数创建用
户线程,新线程就属于调用ReadFile的进程。
系统线程不属于当前用户进程,而属于系统进程。系统进程是操作系统中一个特殊的
进程。这个进程的ID - -般为4。
NTSTATUS PsCreateSystemThread(
PHANDLE ThreadHandle, //新创建线程的句柄
ULONG DesiredAccess, //创建的权限
POBJECT_ATTRIBUTES ObjectAttributes, //该线程的属性,一般设置为NULL
HANDLE ProcessHandle,// 第四个参数ProcessHandle指定是创建用户线程还是系统进程。如果该值为NULL,则为创建系统线程。如果该值是一个进程句柄,则新创建的线程属于这个指定的进程。DDK提供的宏NtCurrentProcess可以得到当前进程的句柄。
PCLIENT_ID ClientId, //该线程ID
PKSTART_ROUTINE StartRoutine, //新线程的运行地址
PVOID StartContext //新线程接收的参数
);
在内核模式下,创建的线程必须用函数PsTerminateSystemThread强制线程结束,否则该线程是无法自动退出的。
内核模式下的事件对象
使用事件对象前需要调用KeInitializeEvent对事件对象进行初始化。
void KeInitializeEvent(
PRKEVENT Event,// 初始化事件对象的指针
EVENT_TYPE Type,//指定事件的类型是NotificationEvent(通知类型) 还是SynchronizationEvent(同步)
BOOLEAN State //指定事件的初始状态。True表示已发出信号的状态。
);
如果创建的事件对象是“通知事件”,当事件对象变为激发态时,程序员需要手动将
其改回未激发态。如果创建的事件对象是“同步事件”,当事件对象为激发态时,如遇到
KeWaitForXX等内核函数,事件对象则自动变回未激发态。
事件对象的使用
VOID kThreadProc(PVOID pEvent)
{
KeSetEvent( pEvent,
IO_NO_INCREMENT,
FALSE //是否立即调用KeWaitFOrXXX函数,如果为真,该函数返回而不降低IRQL,不会发生线程切换,直接调用WaitForxxx
);
DbgPrint("线程正在运行\n");
//在内核中,需要自己结束线程
PsTerminateSystemThread(STATUS_SUCCESS);
}
VOID CreateThreadProc()
{
KEVENT kEvent;
HANDLE hThread;
KeInitializeEvent(&kEvent, NotificationEvent, FALSE);
PsCreateSystemThread(&hThread,0,0,NtCurrentProcess(),0,kThreadProc,&kEvent);
//如果这里不使用等待,KeSetEvent使用的pEvent就会是无效的,因为它是在这个函数中。
KeWaitForSingleObject(&kEvent, Executive, KernelMode,FALSE,NULL);
DbgPrint("完成线程等待\n");
}
在两个不同的驱动中交互事件对象
这就需要创建带有名字的事件对象,使用IoCreateNotificationEvent和IoCreateSynchronizationEvent
函数。
内核模式下的信号量
同样的使用信号量也需要使用KeInitializeSemaphore来进行初始化
void KeInitializeSemaphore(
PRKSEMAPHORE Semaphore, //信号量的指针
LONG Count, //初始化信号的个数
LONG Limit //信号量计数的上限
);
信号量可以一次性通知多个WaitForxxx函数。
内核模式下的互斥体
初始化互斥体对象使用KeInitializeMutex函数。
void KeInitializeMutex(
PRKMUTEX Mutex, //互斥体对象指针
ULONG Level //保留值,一般设置为0
);
互斥体是与线程相关的。在一个线程获得该互斥体对象是,别的同时也在等待该互斥体对象的线程是不能执行的,必须在该线程调用KeReleaseMutex释放互斥体后吗,才能被别的线程拥有。
快速互斥体
快速互斥体的执行速度比普通互斥体块,唯一的区别是快速互斥体不能被同一个线程拥有两次。
除此之外,对快速互斥体的初始化、获取和释放对应的内核函数也和普通互斥体不同。
初始化快速互斥体的内核函数是ExInitializeFastMutex, 获取快速互斥体的内核函数是
ExAcquireFastMutex,释放快速互斥体的内核函数是ExReleaseFastMutex。下面的例子演示
了如何在驱动程序中使用快速互斥体。
使用自旋锁进行同步
对于要同步的代码,需要用同一把自旋锁进行同步。如果程序得到了自旋锁,其他程
序希望获取自旋锁时,则不停地进入自旋状态。获得自旋锁的内核函数是KeAcquire
SpinLock。直到自旋锁被释放后,另外的程序才能获取到自旋锁。释放自旋锁的内核函数
是KeReleaseSpinIock。
使用互锁操作进行同步
DDK提供了两类互锁操作来提供简单的同步处理,一类 是InterlockedXX 函数,另一
类是ExInterlockedXX函数。其中,InterlockedXX 系列的函数不通过自旋锁实现,而
ExInterlockedXX系列函数通过自旋锁实现。InterlockedXX 系列函数不需要程序员提供自
旋锁,内部不会提升IRQL,因此InterlockedXX函数可以操作非分页的数据,也可以操作
分页的数据。而ExInterlockedXX需要程序员提供一个自旋锁,内部依靠这个自旋锁实现
同步,所有ExInterlockedXX不能操作分页内存的数据。(因为操作了内存,且提升了IRQL 如果内存不存在,处理页异常的程序是不能执行的,从而引发错误蓝屏)
ExInterLockedXX系列互锁操作函数
内核函数 | 功能 |
---|---|
ExInterlockedAddlargeInteger64 | 64位整数加法互锁操作 |
ExInterlockedAddLargeStatistic64 | 64位整数加法互锁操作 |
ExInterlockedAddUlong | 32位整数加法互锁操作 |
ExInterlockedAllocateFromZone | 分配互锁操作 |
ExInterlockedCompareExchange64 | 两个32位整数互换互锁操作 |
ExInterlockedDecrementLong | 32位整数减法互锁操作 |
ExInterlockedExchangeAddLargelnteger | 64位整数加法互锁操作 |
ExInterlockedExchangeUlong | 两个整数交换互锁操作 |
ExInterlockedFlushSList | 删除链表全部元素的互锁操作 |
ExInterlockedIncrementLong | 32位整数自增互锁操作 |
ExInterlockedInsertHeadList | 插入双向链表互锁操作 |
ExInterlockedInsertTaillist | 插入双向链表互锁操作 |
ExInterlockedPopEntryList | 删除单向链表互锁操作 |
ExInterlockedPopEntrySList | 删除单向链表互锁操作 |
ExInterlockedPushEntryList | 插入单向链表互锁操作 |
ExInterlockedPushEntrySList | 插入单向链表互锁操作 |
ExInterlockedRemoveHeadList | 插入双向链表互锁操作 |
InterLockedXX系列互锁操作函数
内核函数 | 功能 |
---|---|
InterlockedCompareExchange | 比较互锁操作 |
InterlockedCompareExchangePointer | 比较互锁操作 |
InterlockedDecrement | 整型自减互锁操作 |
InterlockedExchange | 整型交换互锁操作 |
InterlockedExchangeAdd | 两个整型相加互锁操作 |
InterlockedExchangePointer | 为指针赋值互锁操作 |
InterlockedIncrement | 整型自增互锁操作 |