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

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

楼主#
更多 发布于:2008-06-07 11:31
本文作者是楚狂人。有问题请联系QQ16191945,msn walled_river@hotmail.com

Windows驱动编程基础教程(6.1-6.2)

6.1 使用线程
    
    有时候需要使用线程来完成一个或者一组任务。这些任务可能耗时过长,而开发者又不想让当前系统停止下来等待。在驱动中停止等待很容易使整个系统陷入“停顿”,最后可能只能重启电脑。但一个单独的线程长期等待,还不至于对系统造成致命的影响。另一些任务是希望长期、不断的执行,比如不断写入日志。为此启动一个特殊的线程来执行它们是最好的方法。
    在驱动中生成的线程一般是系统线程。系统线程所在的进程名为“System”。用到的内核API函数原型如下:

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

    这个函数的参数也很多。不过作者本人的使用经验如下:ThreadHandle用来返回句柄。放入一个句柄指针即可。DesiredAccess总是填写0。后面三个参数都填写NULL。最后的两个参数一个用于改线程启动的时候执行的函数。一个用于传入该函数的参数。
    下面要关心的就是那个启动函数的原型。这个原型比起定时器回调函数倒是异常的简单,没有任何多余的东西:
    
    VOID CustomThreadProc(IN PVOID context)
    
    可以传入一个参数,就是那个context。context就是PsCreateSystemThread中的StartContext。值得注意的是,线程的结束应该在线程中自己调用PsTerminateSystemThread来完成。此外得到的句柄也必须要用ZwClose来关闭。但是请注意:关闭句柄并不结束线程。
    下面举一个例子。这个例子传递一个字符串指针到一个线程中打印一下。然后结束该线程。当然打印字符串这种事情没有必要单独开一个线程来做。这里只是一个简单的示例。请注意,这个代码中有一个隐藏的错误,请读者指出这个错误是什么:

    // 我的线程函数。传入一个参数,这个参数是一个字符串。
    VOID MyThreadProc(PVOID context)
    {
        PUNICODE_STRING str = (PUNICODE_STRING)context;
        // 打印字符串
        KdPrint((“PrintInMyThread:%wZ\r\n”,str));
        // 结束自己。
        PsTerminateSystemThread(STATUS_SUCCESS);
    }

    VOID MyFunction()
    {
        UNICODE_STRING str = RTL_CONSTANT_STRING(L“Hello!”);
        HANDLE thread = NULL;
        NTSTATUS status;
    
        status = PsCreateSystemThread(
            &thread,0L,NULL,NULL,NULL,MyThreadProc,(PVOID)&str);
        if(!NT_SUCCESS(status))
        {
            // 错误处理。
            …
        }
        // 如果成功了,可以继续做自己的事。之后得到的句柄要关闭
        ZwClose(thread);
    }
    
    以上错误之处在于:MyThreadProc执行的时候,MyFunction可能已经执行完毕了。执行完毕之后,堆栈中的str已经无效。此时再执行KdPrint去打印str一定会蓝屏。这也是一个非常隐蔽,但是非常容易犯下的错误。
    合理的方法是是在堆中分配str的空间。或者str必须在全局空间中。请读者自己写出正确的方法。
    但是读者会发现,以上的写法在正确的代码中也是常见的。原因是这样做的时候,在PsCreateSystemThread结束之后,开发者会在后面加上一个等待线程结束的语句。
    这样就没有任何问题了,因为在这个线程结束之前,这个函数都不会执行完毕,所以栈内存空间不会失效。
    这样做的目的一般不是为了让任务并发。而是为了利用线程上下文环境而做的特殊处理。比如防止重入等等。在后面的章节读者会学到这方面的技巧。
    如何等待线程结束在后面1.6.3“使用事件通知”中进一步的讲述。
    
6.2 在线程中睡眠    

    许多读者一定使用过Sleep函数。这能使程序停下一段时间。许多需要连续、长期执行,但是又不希望占太多CPU使用率的任务,可以在中间加入睡眠。这样能使CPU使用率大大降低。即使睡眠的时间非常短(几十个毫秒)。
    在驱动中也可以睡眠。使用到的内核函数的原型如下:

    NTSTATUS
      KeDelayExecutionThread(
        IN KPROCESSOR_MODE  WaitMode,
        IN BOOLEAN  Alertable,
        IN PLARGE_INTEGER  Interval);

    这个函数的参数简单明了。WaitMode请总是填写KernelMode,因为现在是在内核编程中使用。Alertable表示是否允许线程报警(用于重新唤醒)。但是目前没有必要用到这么高级的功能,请总是填写FALSE。剩下的就是Interval了,表明要睡眠多久。
    但是这个看似简单的参数说明起来却异常的复杂。为此作者建议读者使用下面简单的睡眠函数,这个函数可以指定睡眠多少毫秒,而没有必要自己去换算时间(这个函数中有睡眠时间的转换):
    
    #define DELAY_ONE_MICROSECOND     (-10)
    #define DELAY_ONE_MILLISECOND    (DELAY_ONE_MICROSECOND*1000)
    VOID MySleep(LONG msec)
    {
        LARGE_INTEGER my_interval;
        my_interval.QuadPart = DELAY_ONE_MILLISECOND;
        my_interval.QuadPart *= msec;
        KeDelayExecutionThread(KernelMode,0,&my_interval);
    }
    
    当然要睡眠几秒也是可以的,1毫秒为千分之一秒。所以乘以1000就可以表示秒数。
    在一个线程中用循环进行睡眠,也可以实现一个自己的定时器。考虑前面说的定时器的缺点:中断级较高,有一些事情不能做。在线程中用循环睡眠,每次睡眠结束之后调用自己的回调函数,也可以起到类似的效果。而且系统线程执行中是Passive中断级。睡眠之后依然是这个中断级,所以不像前面提到的定时器那样有限制。
    请读者自己写出用线程+睡眠来实现定时器的例子。

最新喜欢:

greenpeacegreenp...
lonkil
驱动牛犊
驱动牛犊
  • 注册日期2008-04-24
  • 最后登录2010-07-08
  • 粉丝0
  • 关注0
  • 积分5分
  • 威望13点
  • 贡献值0点
  • 好评度7点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2008-06-07 16:04
感谢分享
http://www.vcfans.com
alwaysrun
驱动小牛
驱动小牛
  • 注册日期2006-06-01
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分1059分
  • 威望752点
  • 贡献值1点
  • 好评度98点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2008-06-10 10:21
顶一下
一颗平常的心!
zhoujiamurong
驱动小牛
驱动小牛
  • 注册日期2006-03-20
  • 最后登录2009-05-06
  • 粉丝4
  • 关注0
  • 积分1081分
  • 威望360点
  • 贡献值0点
  • 好评度215点
  • 原创分0分
  • 专家分0分
地板#
发布于:2008-07-09 10:12
谢谢指教。
zhangjian82100
驱动牛犊
驱动牛犊
  • 注册日期2009-06-25
  • 最后登录2009-08-09
  • 粉丝0
  • 关注0
  • 积分9分
  • 威望91点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2009-08-03 01:26
竹林蹊径 什么时候出版啊
游客

返回顶部