XiangXiangRen
总版主
总版主
  • 注册日期2003-02-22
  • 最后登录2015-09-01
  • 粉丝13
  • 关注0
  • 积分1042分
  • 威望472点
  • 贡献值1点
  • 好评度145点
  • 原创分13分
  • 专家分1分
阅读:2428回复:4

Windows驱动编程基础教程(连载11)

楼主#
更多 发布于:2008-06-07 11:32
本文作者是楚狂人。有问题请联系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之后才返回。缺点是这个函数不能起到并发执行的作用。

最新喜欢:

greenpeacegreenp...
guoxiabin
驱动牛犊
驱动牛犊
  • 注册日期2007-10-27
  • 最后登录2013-12-05
  • 粉丝0
  • 关注0
  • 积分16分
  • 威望150点
  • 贡献值0点
  • 好评度12点
  • 原创分1分
  • 专家分0分
沙发#
发布于:2008-06-07 13:24
刚好学习这块了,感谢楚大哥
alwaysrun
驱动小牛
驱动小牛
  • 注册日期2006-06-01
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分1059分
  • 威望752点
  • 贡献值1点
  • 好评度98点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2008-06-10 10:22
一颗平常的心!
zhoujiamurong
驱动小牛
驱动小牛
  • 注册日期2006-03-20
  • 最后登录2009-05-06
  • 粉丝4
  • 关注0
  • 积分1081分
  • 威望360点
  • 贡献值0点
  • 好评度215点
  • 原创分0分
  • 专家分0分
地板#
发布于:2008-06-10 15:03
踩个脚印
hhyDriver
驱动小牛
驱动小牛
  • 注册日期2007-06-06
  • 最后登录2009-01-19
  • 粉丝0
  • 关注0
  • 积分154分
  • 威望150点
  • 贡献值0点
  • 好评度146点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2008-06-11 08:47
ding ding ding
游客

返回顶部