本博文由CSDN博主zuishikonghuan所作,版权归zuishikonghuan所有,转载请注明出处:http://blog.csdn.net/zuishikonghuan/article/details/51301896
在内核模式下创建线程
在驱动程序中创建线程的方法是调用 PsCreateSystemThread 内核函数,此函数即可以创建系统线程,也可以创建用户线程。此函数的原型如下:
NTSTATUS PsCreateSystemThread(
_Out_ PHANDLE ThreadHandle,
_In_ ULONG DesiredAccess,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ HANDLE ProcessHandle,
_Out_opt_ PCLIENT_ID ClientId,
_In_ PKSTART_ROUTINE StartRoutine,
_In_opt_ PVOID StartContext
);
- 参数1:一个 HANDLE 类型的指针,用于输出创建的线程句柄。此句柄必须在不需要使用时通过 ZwClose 释放。
- 参数2:创建线程的权限,一般设置为0。
- 参数3:指向指定对象的属性的结构,一般设置为 NULL 。
- 参数4:如果为 NULL ,则线程属于系统。如果指定一个进程句柄,则线程属于该进程,通过 NtCurrentProcess 宏可以得到当前进程句柄。
- 参数5:用于接收客户端ID,一般为 NULL 。
- 参数6:线程的启动函数指针。
- 参数7:传递给启动函数的数据。
线程启动函数原型:
KSTART_ROUTINE ThreadStart;
VOID ThreadStart(
_In_ PVOID StartContext
)
{ ... }
需要注意,和用户模式线程不同,线程必须调用 PsTerminateSystemThread (或系统关机)才会退出,而不是函数返回即退出。
内核模式下的等待
驱动程序进行等待可以通过 KeWaitForSingleObject 和 KeWaitForMultipleObjects 进行等待,其实用户模式下的 WaitForSingleObject 和 WaitForMultipleObjects 就是调用了这两个函数,先来看看函数原型:
NTSTATUS KeWaitForSingleObject(
_In_ PVOID Object,
_In_ KWAIT_REASON WaitReason,
_In_ KPROCESSOR_MODE WaitMode,
_In_ BOOLEAN Alertable,
_In_opt_ PLARGE_INTEGER Timeout
);
NTSTATUS KeWaitForMultipleObjects(
_In_ ULONG Count,
_In_ PVOID Object[],
_In_ WAIT_TYPE WaitType,
_In_ KWAIT_REASON WaitReason,
_In_ KPROCESSOR_MODE WaitMode,
_In_ BOOLEAN Alertable,
_In_opt_ PLARGE_INTEGER Timeout,
_Out_opt_ PKWAIT_BLOCK WaitBlockArray
);
解释一下 KeWaitForSingleObject :
- 参数1:同步对象的指针。(不是句柄)
- 参数2:指定等待的原因。驱动程序应将该值设置为 Executive ,除非是做代表用户的工作,并在用户线程的上下文中,在这种情况下,应该将该值设置为运行 UserRequest 。
- 参数3:说明是在内核模式等待还是在用户模式等待(KernelMode or UserMode)一般设置为 KernelMode 。
- 参数4:设置等待是否“警惕”,一般为 FALSE 。
- 参数5:超时时间,若为 NULL ,则为无限等待(还记得上一篇中说过 IRQL 不小于 DISPATCH_LEVEL 的线程不能进行没有超时的等待操作吗)指向64位整数指针时,若是正数,则为从 January 1, 1601 ,若为负数,则从现在开始计时。
内核模式下的同步对象
和用户模式下的同步对象大致一样,因为用户模式同步对象就是通过调用内核同步对象函数创建的。
在应用程序中我们只能得到同步对象的指针,而在驱动程序中我们可以直接使用事件对象的指针!
对于各函数我想我不必一个个解释了,MSDN说的很清楚了,这里只起一个引导和区分作用。
1。事件
创建事件:KeInitializeEvent
创建命名事件:IoCreateNotificationEvent 和 IoCreateSynchronizationEvent
命名的事件对象的好处是,可以方便在不同驱动中找到事件对象的指针。
NotificationEvent(通知事件)和 SynchronizationEvent(同步事件)的区别:前者由未激发变为激发时,需要手动变成未激发,而后者激发后调用 KeWaitFor… 函数就会自动变为未激发。
2。自旋锁
获取自旋锁:KeAcquireSpinLock
释放自旋锁:KeReleaseSpinLock
自旋锁有“锁住”和“解锁”状态,当线程获取自旋锁时,如果处于解锁状态就可以被获取,获取会自动锁住自旋锁,必须释放后才能解锁;如果获取时处于锁住状态,那么线程就会不停地“自旋”,即线程不停地常数获取自旋锁,直到获取到为止。自旋锁不能工作在 IRQL DISPATCH_LEVEL 中断请求级。
特别需要注意:线程如果等待一个“事件”对象,那么线程就会休眠,操作系统会去调度其他线程,而获取自旋锁不同,线程会一直“自旋”,会浪费宝贵的 CPU 时间,因此获取自旋锁后到释放之前的时间不要过长。
3。互斥体/快速互斥体
创建互斥体:KeInitializeMutex
释放互斥体:KeReleaseMutex
创建快速互斥体:ExInitializeFastMutex
释放快速互斥体:ExReleaseFastMutex
快速互斥体不能被线程递归获取。
只有一个线程可以占用互斥体,获取互斥体的线程如果不释放互斥体,其他线程就不能得到互斥体,线程获取到一个互斥体时,互斥体变为激发,释放一个互斥体时,互斥体变为未激发。互斥体未激发时,等待互斥体就可以获取他,激发时则会使线程休眠,直到指定互斥体被释放后,才会有且仅有一个等待该互斥体的线程获取该互斥体。
4。信号量
创建信号量:KeInitializeSemaphore
释放信号量: KeReleaseSemaphore
信号量同样有激发和未激发两种状态,他的特殊之处是内部维护一个计数器,只要计数器不小于1就是激发状态,否则是未激发状态。