总版主
|
阅读:2598回复:4
Windows驱动编程基础教程(连载11)
本文作者是楚狂人。有问题请联系QQ16191945,msn walled_river@hotmail.com
Windows驱动编程基础教程(6.3) 6.3 使用事件通知 一些读者可能熟悉“事件驱动”编程技术。但是这里的“事件”与之不同。内核中的事件是一个数据结构。这个结构的指针可以当作一个参数传入一个等待函数中。如果这个事件不被“设置”,则这个等待函数不会返回,这个线程被阻塞。如果这个事件被“设置”,则等待结束,可以继续下去。 这常常用于多个线程之间的同步。如果一个线程需要等待另一个线程完成某事后才能做某事,则可以使用事件等待。另一个线程完成后设置事件即可。 这个数据结构是KEVENT。读者没有必要去了解其内部结构。这个结构总是用KeInitlizeEvent初始化。这个函数原型如下: 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用于提升优先权。目前设置为0即可。Wait表示是否后面马上紧接着一个KeWaitSingleObject来等待这个事件。一般设置为TRUE。(事件初始化之后,一般就要开始等待了。) 使用事件的简单代码如下: // 定义一个事件 KEVENT event; // 事件初始化 KeInitializeEvent(&event,SynchronizationEvent,TRUE); …… // 事件初始化之后就可以使用了。在一个函数中,你可以等待某 // 个事件。如果这个事件没有被人设置,那就会阻塞在这里继续 // 等待。 KeWaitForSingleObject(&event,Executive,KernelMode,0,0); …… // 这是另一个地方,有人设置这个事件。只要一设置这个事件, // 前面等待的地方,将继续执行。 KeSetEvent(&event); 由于在KeInitializeEvent中使用了SynchronizationEvent,导致这个事件成为所谓的“自动重设”事件。一个事件如果被设置,那么所有KeWaitForSingleObject等待这个事件的地方都会通过。如果要能继续重复使用这个时间,必须重设(Reset)这个事件。当KeInitializeEvent中第二个参数被设置为NotificationEvent的时候,这个事件必须要手动重设才能使用。手动重设使用函数KeResetEvent。 LONG KeResetEvent( IN PRKEVENT Event ); 如果这个事件初始化的时候是SynchronizationEvent事件,那么只有一个线程的KeWaitForSingleObject可以通过。通过之后被自动重设。那么其他的线程就只能继续等待了。这可以起到一个同步作用。所以叫做同步事件。不能起到同步作用的是通知事件(NotificationEvent)。请注意不能用手工设置通知事件的方法来取代同步事件。请读者思考一下这是为什么。 回忆前面的1.6.1 “使用线程”的最后的例子。在那里曾经有一个需求:就是等待线程中的函数KdPrint结束之后,外面生成线程的函数再返回。 这可以通过一个事件来实现:线程中打印结束之后,设置事件。外面的函数再返回。为了编码简单我使用了一个静态变量做事件。这种方法在线程同步中用得极多,请务必熟练掌握: static KEVENT s_event; // 我的线程函数。传入一个参数,这个参数是一个字符串。 VOID MyThreadProc(PVOID context) { PUNICODE_STRING str = (PUNICODE_STRING)context; KdPrint((“PrintInMyThread:%wZ\r\n”,str)); KeSetEvent(&s_event); // 在这里设置事件。 PsTerminateSystemThread(STATUS_SUCCESS); } // 生成线程的函数: VOID MyFunction() { UNICODE_STRING str = RTL_CONSTANT_STRING(L“Hello!”); HANDLE thread = NULL; NTSTATUS status; KeInitializeEvent(&event,SynchronizationEvent,TRUE); // 初始化事件 status = PsCreateSystemThread( &thread,0L,NULL,NULL,NULL,MyThreadProc,(PVOID)&str); if(!NT_SUCCESS(status)) { // 错误处理。 … } ZwClose(thread); // 等待事件结束再返回: KeWaitForSingleObject(&s_event,Executive,KernelMode,0,0); } 实际上等待线程结束并不一定要用事件。线程本身也可以当作一个事件来等待。但是这里为了演示事件的用法而使用了事件。以上的方法调用线程则不必担心str的内存空间会无效了。因为这个函数在线程执行完KdPrint之后才返回。缺点是这个函数不能起到并发执行的作用。 |
最新喜欢:greenp... |
沙发#
发布于:2008-06-07 13:24
刚好学习这块了,感谢楚大哥
|
|
板凳#
发布于:2008-06-10 10:22
|
|
|
驱动小牛
|
地板#
发布于:2008-06-10 15:03
踩个脚印
|
地下室#
发布于:2008-06-11 08:47
ding ding ding
|
|