• 标 题:Windows驱动开发技术详解学习笔记---同步调用和异步调用的执行顺序(单一请求)
  • 作 者:原去如此
  • 时 间:2010-10-10 19:35:09
  • 链 接:http://bbs.pediy.com/showthread.php?t=121974

一个月前开始学习Windows驱动开发,断断续续将《Windows驱动开发技术详解》这本书概读了两遍,将其中的一些体会分享给大家,这次介绍一下最简单的同步调用和异步调用的执行顺序问题。
我们知道驱动基本上由DriverEntry例程(这里引用书上的术语,就不称作函数了)和若干派遣例程组成,这里只讲最简单的,其余的以后再说。
DriverEntry例程是由System进程在驱动被加载的时机被调用的。其中主要注册派遣例程,以方便后续的用户进程能够访问它们。

同步这个概念就不说了,我们来看看下面基本的4种调用。
1)同步调用(驱动支持同步模式)
这种情况下,同步等待是由Windows来负责等待操作的完成,而驱动仅仅在派遣例程中的调用IOCompleteRequest来完成该操作。


2)异步调用(驱动支持同步模式)
这种情况下,同步等待是Windows就不负责了,等待必须由用户负责,而驱动仅仅在派遣例程中的调用IOCompleteRequest来完成该操作。
从这里可以看到,如果驱动层只支持同步模式的话,应用程序使用同步和异步没有多大驱动,因为当前调用返回值,底层动作已经完毕。反而多了等待事件的操作,还不如用同步方式。

这里需要强调一下,在派遣例程返回之前,用户的IO调用是不会返回的。好像好多人对此不是很理解。


3)同步调用(驱动支持异步模式)
和1)一样,同步等待是由Windows来负责等待操作的完成,不过在派遣例程返回时,该IO操作还没有完成。等到后续的DPC例程执行完时,本次IO操作才算完毕,用户的的调用这时候才真正返回。
这里需要注意的有两点:
一:DPC的执行线程上下文已经不一定是该调用的用户线程,因此有些需要访问用户内存空间的代码就会出问题了。
二:DPC的执行级别是在DISPATCH_LEVEL上的,执行期间不会发生线程切换。(书上这么说的,不过如果在其中有等待操作的话,还是会发生线程切换的,因为是当前线程主动放弃CPU时间的。
【当前线程<线程甲>:我是无辜的,为什么让我暂停!!! <----没名字的家伙,停的就是你-_-】)

4)异步调用(驱动支持异步模式)
从图上可以看到,只有在这种方式下,用户才能在底层执行的同时来做点兼职工作。


介绍完基本的调用方式,我们把取消例程加上后再分析一下。由于仅支持同步模式的驱动以及用户同步调用时,IO操作已经完成,因此这里只考虑异步调用(驱动支持异步模式)方式。
其中可以分为取消成功和取消失败两种情况:
1)取消成功
我们将上面的兼职工作修改成取消IO操作,从图上可以看到,驱动执行Cancel例程时,会将DPC例程取消,同时将本次IO操作以Cancelled返回。然后用户继续等待事件,当然也已经将信号设置上了。
注意,虽然已经调用了CancelIO,但是,等待事件不可跳过,因为还有取消失败的情况发生。
还有Cancel例程的运行级别是在APC_LEVEL上的。


2)取消失败
我们发现,Windows打算执行Cancel例程时,本次IO操作已经完毕。该请求已经不存在了,当然以失败而告终。
注意DPC中应该将该IO操作的Cancel例程指针设定为NULL。


到此为止,上述的执行顺序还算比较简单,原因是用户调用还处于单线程模式下。
下面稍微加大一点难度,我们把CancelIO的调用移动到另外一个线程中去,这样CancelIO的调用时机点和本次IO操作的重叠契机又多了两种情况。我们先来看看和单线程相同的两种情况。
除了Cancel例程在另一个线程上下文中执行之外,和单线程方式没啥区别。
1)取消成功

2)取消失败


多出来的两种情况是,IO操作执行过程中(IO调用还未返回),由另一个线程取消本次IO。这里也会存在两种情况,成功或者失败。
1)取消失败
IO操作还未到达派遣例程时,当然调用无效啦,就像平白无辜调用CancelIO一样。见图。


2)取消成功
这次的情况是两个例程赶到一块去了,大家注意做好同步控制。具体还得看派遣例程内部如何实现。实现得好的话,这种情况和上面的某一种属于同一类。


到此为止,有几个概念需要明确一下。
1)例程
有时可以把它当作用户态的线程在内核上的一种映射。当然不一定是一对一的。
2)运行级别(IRQL)
书上也有介绍,大家记住处于DISPATCH_LEVEL的运行级别下是不会发生线程切换的就行。
不过有一点书上也没有提到,就是处理APC_LEVEL的运行级别下会不会发生线程切换呢?好像网上也没有找到答案。

由此看来,只要掌握同步和异步的概念,并且对系统已线程为单位进行执行的基础知识没有问题的话,也不是很难理解的。
关于多IO操作的StartIO例程的执行顺序,下回学好了再写,今天就到此为止,希望大家共同进步。