线程与事件

145 阅读3分钟

线程与事件

当需要一个单独的线程来完成一些事情的时候,很容易出现像”父进程被销毁,而子进程依旧在执行的情况”,会出现子进程需要的数据已经被销毁,而最坏的打算是这片内存又恰好被别的程序所占用

或者是某些任务执行时间过长,放在主线程中不太合适,而单独开一个线程来完成

又或者是需要长期不断地执行的任务,比如不断地写入日志

在驱动中生成的线程一般是系统线程。系统线程所在的进程名为“System”,用到的内核API函数原型如下:

NTSTATUS PsCreateSystemThread(OUT PHANDLE ThreadHandle,IN ULONG DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,IN HANDLE ProcessHandle OPTIONAL,IN PKSTART_ROUTINE StartRoutine,IN PVOID StartContext)

下面举一个例子。这个例子传递一个字符串指针到一个线程中打印,然后结束该线程。当然,打印字符串这种事情没有必要单独开一个线程来做,这里只是一个简单的示例。请注意,这个代码中有一个隐藏的错误

MyThreadProc执行时,MyFunction可能已经执行完毕了。执行完毕之后,堆栈中的str已经无效,此时再执行KdPrint去打印str一定会蓝屏。这是一个非常隐蔽,但是非常容易犯的错误。
合理的方法是在堆中分配str的空间,或者说str必须在全局空间中。请读者自己写出正确的方法。
但是读者会发现,以上的写法在正确的代码中也是常见的。如果这样做,在PsCreateSystemThread结束之后,开发者会在后面加上一个等待线程结束的语句。
这样就没有任何问题了,因为在这个线程结束之前,函数都不会执行完毕,所以栈内存空间不会失效。
这样做的目的一般不是为了让任务并发,而是为了利用线程上下文环境而进行特殊处理,比如防止重入等。

同步事件

内核中的事件是一个数据结构,这个结构的指针可以当作一个参数传入一个等待函数中。如果这个事件不被“设置”,那么这个等待函数就不会返回,这个线程将被阻塞;如果这个事件被“设置”,那么等待结束即可继续下去。
事件常常用于多个线程之间的同步。如果一个线程需要等待另一个线程完成某事后才能做某事,则可以使用事件等待,另一个线程完成后设置事件即可。
其数据结构是KEVENT.使用KeInitizeEvent初始化

函数原型: VOID KeInitializeEvent ( IN PRKEVENT Event,IN EVENT_TYPE Type, IN BOOLEAN State);

第一个参数是要初始化的事件;第二个参数是事件类型,详见后面的解释;第三个参数是初始化状态,一般设置为FALSE,也就是未设置状态,这样等待者需要等待设置之后才能通过。
该事件对象不需要销毁。

设置事件使用函数KeSetEvent.函数原型: LONG KeSetEvent( IN PRKEVENT Event,IN KPRIORITY Increment, IN BOOLEAN Wait);

Event是要设置的事件;Increment用于提升优先权;Wait表示是否后面马上紧接着一个KeWaitSingleObject来等待这个事件,一般设置为TRUE(事件初始化之后,一般就要开始等待了)。

使用事件的简单代码:

**

**

\