当前位置:文档之家› 驱动开发历程-驱动入门详解

驱动开发历程-驱动入门详解

驱动开发历程-驱动入门详解
驱动开发历程-驱动入门详解

目录

热身阶段 (2)

问1:什么是过滤驱动?(2009-4-15) (2)

问2:什么是IRP?(2009-4-15) (3)

问3:驱动栈,设备栈?(2009-4-15) (6)

问4:文件系统过滤驱动(FSFD)为什么能过滤文件系统(FSD)?(2009-4-16) (8)

问5:怎么用好DDK(或WDK,现在起本书只说WDK)?(2009-4-16) (11)

Legacy驱动阶段 (12)

问6:DriverEntry和DriverUnload是干嘛的?(2009-04-20) (12)

问7:SfCreate(2009-04-20) (12)

问8:SfDisplayCreateFileName(2009-04-23) (14)

问9:fastio系列例程(2009-04-28) (15)

问10:总结sfilter(2009-04-28) (25)

问11:fspyKern.h、fspydef.h和filespy.h(2009-04-28) (26)

问12:fspyHash.c(2009-04-28) (27)

问13:上下文(2009-04-30) (34)

问14:fspyCtx.c(2009-04-28) (39)

问15:FspyLib.c(2009-04-28) (49)

问16:总结filespy(2009-04-30) (64)

问17:legacy驱动透明加解密设计开发示例(2009-04-30) (64)

概要设计 (64)

定位机制设计 (66)

跟踪机制设计 (66)

加解密模块设计 (68)

其他设计 (68)

问18:总结legacy驱动(2009-05-04) (70)

Mini驱动阶段 (71)

问19:passThrough(2009-05-04) (71)

问20:ctx(2009-05-04) (72)

问21:scanner(2009-05-05) (75)

问22:swapBuffers(2009-05-05) (78)

问23:总结mini驱动(2009-05-05) (78)

问24:fastfat(2009-05-05) (79)

后记 ................................................................................................................ 错误!未定义书签。

热身阶段

问1:什么是过滤驱动?(2009-4-15)

经历过驱动开发之后,给我最大的一个认识就是:这里所谓的驱动和没有接触它之前所臆想的驱动有很大的不同。我们最早入手的资料是楚狂人的《Windows 文件系统过滤驱动开发教程(第二版)》和toolflat公开的Sfilter Rc4加解密源码。由于legacy源码动辄几千行,而且没有理论基础显得艰涩难懂。因此我们决定先研究开发教程(这一问中涉及的IRP和IRP_MJ_XXX的概念第2问会讲到)。这本书主要是依照DDK中sfilter的例子,逐步引入文件系统过滤驱动中的基本概念和基本方法,这可以以一种简单的方式尽快让你了解驱动。不同环境下的文件名格式其他资料上都讲解得比较详细,这里不再讨论。

部分解析下《Windows 文件系统过滤驱动开发教程(第二版)》

●第一章实用的东西不多,但对过滤驱动是做什么的、怎么做有一个简要的描述

●第二章主要是驱动入口DirverEntry例程及各种对象。总的来说,DriverEntry例程

代码的各个部分都是既定的,需要修改的只是部分变量和参数。本章需要注意的问

题点是驱动中的各种对象。

DirverObject代表这个驱动实例,它有一组函数指针即dispatch函数的函数指针。

这些函数类似于MFC中对应某个消息的处理函数,它们分别用于处理各自对应的

IRP请求。举例来说(以sfilter为例):SfCreate对应IRP_MJ_CREATE即驱动只要

收到MajorFunctionCode为IRP_MJ_CREATE的IRP就交给SfCreate例程处理。编

写一个驱动的主要任务也就是确定要处理哪些IRP_MJ_XXX的IRP,然后编写相

应的处理例程。CDO(控制设备对象)和DO(设备对象)比较好区分:首先,每个驱

动只对应一个CDO,而可以有多个DO;CDO无设备扩展,DO有设备扩展;CDO

的主要任务是作为某个操作(一般都是用户自定义的IOCTL等)的目标,用于修改

整个驱动的内部配置,而DO是驱动对应某个卷生成的,只有以它为目标的IRP

才会被生成这个DO的驱动处理(这也与驱动栈的概念关联紧密,后面详述)。

再来看本章给出的DriverEntry例程代码片段,主要目的是生成CDO。这里主要有

三个点:1、UNICODE_STRTING的使用,参考《驱动开发基础教程》;2、可用函

数的调用。需要说明的是由于驱动是由C语言编写的,因此大部分C运行时函数

都可以用于驱动编程,但直接使用C运行时库有许多风险,推荐能使用DDK封装

好的函数就尽量多用封装好的,除非你愿意自己做繁琐的版本移植和维护;3、对

某个函数调用所产生的各种结果都要处理到位。假如在用户模式下调用某函数不考

虑其失败的情况,后面的程序要用到该函数返回的某个值(失败时该值无效)时就会

发生错误,严重点也就程序运行时出错被迫终止并弹出一个相对友好的报错对话

框。在内核模式下的编程,假如有异常或错误没有及时处理,一般都会引起

bugcheck,现象就是蓝屏。本例只区分了成功或失败两种状态,在特殊场合,指定

的函数调用的状态可能需要分很多种做相应处理。

●第三章和第3.5章主要讲了分发例程(dispatch routine)和fastio这两种方式。

两种方式都不是小问题,后面会专门分别讲,这里只简述。先说说分发例程。在

legacy驱动模型中,你必须对所有类型的请求都做处理,而不管你需不需要过滤它。

因而有些IRP_MJ_XXX类型的IRP你只需要把它传给驱动栈中在你之下的驱动。

既然处理方式一样,为了避免重复编码的无用功,就需要一个通用的“下传”例程,即例中的SfPassThrough。它没什么可说的,就只是负责下传当前的IRP。那些需

要处理的IRP类型,就得按照例子中的示范给出对应的分发例程,如

DriverObject->MajorFunction[IRP_MJ_CREATE] = SfCreate;这样只要驱动接收到

IRP_MJ_CREATE类型的IRP就会把它交给SfCreate例程处理。断言的用法与用户

模式对应,不再赘述。这里可以形象地认为邮局(驱动)收到邮件(IRP),邮局根据邮

件上收信人的邮编和地址(MajorFunction和MinorFunction),把邮件交给收信人

(SfCreate)做处理。这里只是比喻,IRP最终的“收件人”并不一定是当前这个过滤

驱动的分发例程。

Fastio是cache调用引发的,且没有IRP。你可以认为fastio操作的数据一定是在缓

存中。也就是说,用户模式下各种IO到内核模式下只走两天路线:IRP和fastio。

假如你只关心磁盘里的数据,那fastio就没什么意义。关于其中出现的内存申请方

面的技巧后面会详述。提到的两种锁在后来对某些数据结构的操作中会经常碰到。

初期只需要知道。

●后面几章操作和编程方面,就示范目的而言讲解地比较详细,其中出现的很多概念

和方法还需专门开问讨论,也不推荐过早考虑实际开发。暂时要接受和理解这本书

中提到的基本理论已经不容易了。

关于sfilter等源码需要另开题目讨论。

总结:

一个驱动(以sfilter为例,这里要解释以sfilter为例的原因:它的数据结构和功能都相对简单,可参考的资料相对较多)主要组成部分有:数据结构、DriverEntry例程及其他各种例程。它可以对自己所绑定的卷上的IO进行相应处理。这就是实际编程中你看到的驱动。

NTFSI中过滤驱动的定义为:一个拦截到一些已有软件模块的请求的中间层驱动。依靠在请求到达目标前截获请求,过滤驱动就有机会扩展或修改请求的原始接收者所提供的功能或服务。比照上一段来理解,驱动这个概念就没有那么空了。这里只是帮助你尽量块地融入到驱动的开发环境中。不要只看某一份资料,偏执一处只会令你由无知变为“误知”。

文件系统过滤驱动为什么能起到过滤文件系统的作用就需要有一定的知识基础才好理解了。暂时略。

问2:什么是IRP?(2009-4-15)

问1和许多资料及网页都不断地提及IRP这个名词,对于刚接触驱动的人来说这玩意儿是陌生的,但在将来的学习和开发过程中它是一个关键性的基本概念之一。MS在DDK 中解释了IRP及其结构,但有所保留。下面用到的驱动栈概念会在问3中详述。

NT用一个基于包的结构来描述I/O请求。即任何一个I/O请求都能用一个单一的I/O 请求包也就是IRP来描述。当发出一个I/O系统服务时(比如创建文件或读文件的请求),I/O

管理器就会通过构造一个描述此请求的IRP 并把该IRP 的一个指针传给设备驱动来开始对这个请求的处理。假如OS 想向I/O 管理器和设备驱动完整地描述一个I/O 请求,那么IRP 中保存的信息就可以达到这个目的。

如下图,IRP 可以认为是由两个部分组成的:一个"固定"部分和一个I/O 栈。固定部分包含这个请求的相关信息,有可能不同驱动中的IRP 的固定部分相同,也可能在不同驱动间传递IRP 时不需要关注它。假如某个驱动要处理这个IRP ,那么I/O 栈就包含被指定给它的信息。驱动栈中有几个要处理这个IRP 的驱动,那么I/O 栈中将至少有几个I/O 栈位置。

为避免每一个IRP 都要从NT 的非分页池中分配,I/O 管理器维护一对保存有预分配的IRP 的旁观列表(lookaside list ,其相关知识会专门加以讨论)。NT V4.0中,其中一个旁观列表保存带有一个单一I/O 栈位置的IRP 。另外一个旁观列表保存带有三个I/O 栈位置的IRP 。I/O 管理器总是尽可能地使用这些旁观列表中的IRP 。因此,只有没有可用的IRP ,或者要分配的IRP 所需的I/O 栈位置超过三个时,I/O 管理器才会从非分页池中分配IRP(关于内存分配的问题会专门讨论)。否则,它会尽量使用旁观列表中的IRP 。

MdlAddress

Flags

AssociateIrp.MasterIrp

AssociateIrp.SystemBuffer

IoStatus

RequestMode

Cancel

CancelIrql

CancelRoutine

UserBuffer Tail.Overlay.Thread Tail.Overlay.ListEntry I/O Stack Location3I/O Stack Location2I/O Stack Location1IRP

MajorFunction

MinorFunction

Flags

Control

Parameters

DeviceObject

FileObject

I/O 栈位置

IRP 固定部分IRP 结构示意图IRP 中的I/O 栈

IRP 的固定部分中需要特别关注或有用的域如下:

MdlAddres, UserBuffer 和AssociatedIrp.SystemBuffer – 如果有一个关联I/O 操作请求者

的数据buffer ,则这三个域被用来描述这个buffer 。后面会加以详述。

Flags –顾名思义,这个域包含描述I/O请求的标记。例如,如果在此域中设置了IRP_PAGING_IO标记,则表示IRP所描述的读或写操作是一个分页请求。以此类推,如果设置了IRP_NOCACHE则表示这个请求是在没有中间buffer的情况下被处理的。 IoStatus –当IRP被完成时,完成这个IRP的驱动把IoStatus.Status域设置为I/O操作的完成状态,而把https://www.doczj.com/doc/0918160974.html,rmation域设置为一些要返回给调用者的额外信息。一般,在一个传输请求(读或写等会引起数据传输的请求)中https://www.doczj.com/doc/0918160974.html,rmation 将会包含实际上被读或被写的字节数。

RequestorMode –指示这个请求是从哪种模式(内核模式或用户模式)发起的。

Cancel, CancelIrql, and CancelRoutine –如果正在进行时需要取消这个IRP就会用到它们。Cancel是一个BOOLEAN类型值,当被I/O管理器设置为TRUE时表示这个IRP 正被取消。CancelRoutine是一个指针,在把这个IRP保存到某个队列中时由一个驱动将它设置为指向一个函数,I/O管理器会调用这个函数来让该驱动取消此IRP。因为CancelRoutine是在IRQL DISPA TCH_LEVEL被调用, 而CancelIRQL是驱动应该返回到的IRQL。

Tail.Overlay.Thread –指向请求线程的ETHREAD。

TailOverlay.ListEntry –可能被驱动用来排队的位置,尽管它拥有这个IRP。

I/O栈位置的结构定义见NTDDK.H中的IO_STACK_LOCATION。要获得当前IRP中本驱动的I/O栈位置,驱动得调用函数IoGetCurrentIrpStackLocation(…)。

当I/O管理器开始分配IRP并初始化IRP的固定部分时,它也初始化IRP中的首个I/O 栈位置。这个I/O栈位置中的信息与被传给将会处理这个请求的驱动栈中的首个驱动的信息是一致的。I/O栈位置中有以下域:

MajorFunction –关联这个请求的主I/O函数代码。它从总体上指明了要执行的I/O操作的类型。

MinorFunction –关联这个请求的辅助I/O函数代码。当被使用时,它可以进一步帮助程序员理解I/O请求的目的。大多数设备驱动会忽略它们。

Flags –指定给正被执行的I/O函数的处理标记。

Control –一组指示I/O管理器如何处理这个IRP的标记,由I/O管理器设置并参阅。例如, 若设置了SL_PENDING位(因驱动调用了IoMarkI rpPending(…))就指示给I/O管理器此IRP还需其他处理。类推之, SL_INVOKE_ON_CANCEL, SL_INVOKE_ON_ERROR 和SL_INVOKE_ON_SUCCESS都指示了驱动的I/O完成例程什么时候应该被调用。

Parameters –这个域由几个子成员组成,每一个子成员都是基于MajorFunction被指定。 DeviceObject –这个I/O请求的目标设备对象。

FileObject –关联这个请求的文件对象。

在IRP的固定部分和首个I/O栈位置之后的部分都被适当地初始化,I/O管理器会在其内部对应此MajorFunction的分发入口点中调用驱动栈顶部的驱动。因此,如果I/O管理器刚好已经构造了一个IRP来描述一个读请求,那么它会在它的读分发入口点中调用首个驱动。也就是说,你的驱动写好了,绑定到目标卷上了,何时被调用是I/O管理器说了算的。它可是驱动的“指挥官”,IRP就是它的“电报”。

总结:

这里的IRP相关知识比较完整,理解它们离真正理解IRP还是有一段距离的。IRP中所包含的数据结构都是驱动编程的重要依据。要掌握它还需要花大量的时间在编程过程中体会它运用它,推荐多翻资料对比着看,以加强对它的理解。这里你可以找段相对简单的分发例

程源码,看看这段源码是怎么利用IRP的,利用了多少,为什么每个例程利用的部分都有所不同。但不要急着自己摆弄,没有一定的基础前,很容易做大量的无用功且容易得出错误结论形成“成见”,不利于后面的学习和开发。或许有一天你会推翻你所翻阅的许多资料上提出的说法,也包括这里的某些见解。正确的认识胜于一切。

问3:驱动栈,设备栈?(2009-4-15)

技术人员总是试图用形象的语言把一些结构或者知识描述给不懂的人听,类似于比拟手法,便于理解。但作为开发人员,你需要的是本质,而不是别人给你的虚像(不总是假像),至少让自己确信某个说法是有根据的。包括这里提到的见解也需要找机会自己验证和推敲。最本质的东西就包括基本的数据结构。

关于设备栈很多资料上已经较为详细地描述了,这里只是概要。一般,驱动编程里我们都说你在操作某个卷上的某个文件,而不单独说操作某个文件(这跟具体编程有关系,以后会专门讨论)。也就是说,在某个驱动想确知它是否能截获某个IRP之前,它必须首先确定I/O管理器会把这个IRP发往它所绑定的卷。DO的部分数据结构如下:

typedef struct _DEVICE_OBJECT {

PDRIVER_OBJECT DriverObject;//对应创建这个DO的驱动

PDEVICE_OBJECT NextDevice;//对应当前DO后面的那个DO

PDEVICE_OBJECT AttachedDevice;//对应当前DO绑定的真实设备

……

} DEVICE_OBJECT, *PDEVICE_OBJECT;

把AttachedDevice域指向同一设备的DO连接起来:

DriverObject NextDevice

AttachedDevice

DO DriverObject NextDevice

AttachedDevice

DO 驱动1

驱动2

DriverObject NextDevice

AttachedDevice

DO 驱动3

真实设备

……

设备栈与驱动栈示意图

在不添加任何主观臆断的情况下,AttachedDevice 域指向同一设备的DO 是如图分布的(横排竖排都行,这里选择竖排是想突出“自上而下”的顺序便于理解)。只看DO 的NextDevice 域之间的联系,这些DO 就形成了“天然的”逻辑上的链式结构(暂且不说是设备栈)。从绑定过程上看,越先绑定的DO 越靠近真实设备,在链中的位置就越靠下。从图中,我们还可以观察到关联DO 的各个驱动也因为DO 高低位置的不同而呈现出高低分层的样子,假如你有阅读过驱动加载顺序和绑定设备过程相关的资料,可能体会更多。暂时不准备在这一问中介绍这些。

IRP 的传递是以DO 为目标的,当IRP 进入这条驱动链时(暂且不说是驱动栈),从能截获到这个IRP 的驱动角度看,其传递过程如下图所示(绿色的线为IRP 未被处理前,向下传递的路线;红色的线为IRP 被处理后,向上传递的路线):

DriverObject NextDevice

AttachedDevice

DO1DriverObject NextDevice

AttachedDevice

DO2驱动1

驱动2

DriverObject NextDevice

AttachedDevice

DO3驱动3

真实设备……

IRP 处理过程示意图

……

I/O 管理器根据IRP 来分别调用这条驱动链中各个驱动的顺序大致如此。设备栈和驱动栈的概念不那么空洞了吧?当然,你接触到更多的知识后,会发现这个调用过程随时都可能因为栈中某个驱动的特殊处理而发生变化。比如驱动2强行令IRP 表示I/O 操作失败或成功并返回了,那么在处理这个IRP 的过程中,栈中驱动2下面的驱动3等驱动就不会被I/O 管理器调用。后面会加以详述。

总结:

这里介绍设备栈和驱动栈的目的,只是帮助你理解这两个比较抽象的名词。它也简述了IRP 的传递过程但还并不出完整和准确。你可以比照问2中IRP 结构中的I/O 栈,逻辑上的驱动栈是可以和I/O 栈对应起来的。死记硬背的概念永远是模糊的。

问4:文件系统过滤驱动(FSFD)为什么能过滤文件系统(FSD)?(2009-4-16)

我在刚开始学习文件系统过滤驱动的时候始终都有一个疑问:看的资料一直讲怎么处理

读写等操作的IRP啊,怎么找到数据啊……但我怎么知道我的驱动加载后就能过滤文件系统了?

接上一问,既然某个设备上的驱动是以栈的形式分布的,那么先观察下图:

用户请求

I/O管理器

文件系统驱动

硬件相关

用户请求传递过程示意图

图中略去了其他细节,但不影响理解。文件系统在用户请求处理过程中的作用是很明显的,即它收到I/O管理器传来的操作请求后负责在真实设备上执行该操作(调用硬件驱动等细节暂时略去),并将操作的结果返回给I/O管理器。假如你想截获文件系统驱动准备处理或已经处理的操作,你就必须在图中两条直线指示的区域做处理。

既然文件系统驱动是驱动,自然也在关联硬件的驱动栈中。在驱动栈中,你在文件系统上面的任一位置插入自己的过滤驱动,就是所谓的upper文件系统过滤驱动,在文件系统下面的任一位置就是lower文件系统过滤驱动。

两者的区别和能力从图上就可以分析出来:

●Upper过滤驱动可以在请求到达文件系统前就截获它并做自己的处理,而lower过

滤驱动只能在文件系统做出处理后才能插手;

●Upper过滤驱动可以修改用户进程的意图,而lower过滤驱动则可以修改文件系统

的意图;

●Upper过滤驱动修改经由文件系统处理后请求的完成状态只会对用户模式的进程

产生影响,而lower过滤驱动修改完成状态后会对其上的包括文件系统驱动在内的

所有驱动及用户进程都产生影响;

●Upper过滤驱动需要与I/O管理器及文件系统驱动交互,lower过滤驱动则需要与

文件系统或硬件驱动交互(这里做了简化只是尽可能使之容易理解,可能会有其他

中间驱动参与进来)。

除了上述的区别外,还有许多实际编程过程中会遇到的众多不同点。

用户请求

I/O管理器

Upper FS过滤驱动

文件系统驱动

Lower FS过滤驱动

硬件相关

用户请求传递过程示意图2

多查阅驱动加载顺序及驱动绑定设备过程相关的资料,你就会知道如何把你的过滤驱动放到栈中合适的位置了。

总结:

回顾下问3中讲的驱动栈中的各个驱动的调用过程,发给驱动栈的I/O请求都要先经过upper过滤驱动,然后到达文件系统驱动,最后是lower过滤驱动(暂不考虑硬件驱动等)。结合上面对这两种过滤驱动区别和能力的简要分析,这个“过滤”就与IRP传递过程联系起来了,它就像是一个全自动流水线,在系统运行期间不停地运转。现在你可能会想要证明自己的驱动确实是在工作着的。不过最好先压下这个念头,等卷轴逐步展开至完毕后,全貌就在你眼前了。

问5:怎么用好DDK(或WDK,现在起本书只说WDK)?(2009-4-16)

这里用WDK泛指参考资料。这一问主要也是说说参考资料的运用。

WDK是官方提供的驱动开发指南,它公布的东西可能会有所保留,但一定是MS要负责维护的,一旦有改动MS必须要提供修订通知。它的使用方法可以参照使用MSDN的方法。

许多初学者很容易漏掉一样东西,那就是WDK附带的源码例子。或许这些例子不会直接实现你想要实现的目标,但它们都有极大的参考价值。这些例子用到的数据结构和一些技巧实际上就是WDK之外的“驱动开发指南”。

NTFSI,这本书从诞生之日起就一直是NT文件系统驱动开发人员所能通过正常途径找到的最权威的文字资料。它对NT执行体组件(I/O管理器、虚拟内存管理器、缓存管理器等)、基本数据结构(FileObject、FileControlBlock等)都有详细的介绍,文件系统驱动中几个主要的分发例程和NT系统服务等也都有讲解。总之,如果你想长期从事Windows操作系统中的文件系统驱动(含过滤驱动)开发工作,那么花至少一年时间吃透它是值得的。特别假如你有不错的英语基础,建议直接看英文版,网上有免费下载。

网络社区资源,国外的看https://www.doczj.com/doc/0918160974.html,,国内也有,比如驱动开发网。网页问答的同时,它们也提供了有用资源的下载。但问答中想要“大实话”还得去国外的。形成良好的国内技术交流环境任重道远。交流也得拿捏好分寸,别人花费数月甚至以年为单位的时间才得出了一些成果,这些成果很可能就是某个公司的核心技术,要免费提供给你很不现实。最有效的提问就是你学习和开发过程中遇到了什么具体问题,并且你也查阅了WDK和手头的一些资料,那么把这个问题出现前后的情景和问题出现时的现象以及自己的分析、调用了什么程序出的这问题等完整地提供出来,就比较容易能引起别人的注意。总之,交流的方式有诸多讲究,有些论坛里会介绍请教的技巧,需要提醒的是:你问的人没有义务回答你。

QQ群,只要有专门的技术交流群,那聊天中必定会有很多问题和解决问题的各种答案,这些宝贵的信息在网页上不一定能看到。对某一问题,国内国外很可能都有人在某个地方给出了答案,但国外充分地利用了list这个工具所以会有公开的记录,国内可能只是私聊,一闪而过。尤其一遇上几个技术牛人在群里“激烈地”讨论某个问题,那么有用信息就很可观了。其实每个开发人员都会因为各种原因有交流愿望,就看你“够不够格”。

总结:

这一问,东西不多,主要是提个醒,拿到需求就埋头写代码不是个好习惯。其结果往往是费时费力,到一定阶段又得推倒重来。另外学习和开发过程中,要有目的性。盲目地学习和盲目地开发都是很“致命的”。总是停留于知识介绍,估计强调再多也嫌烦。只有在实际开发过程中碰到,你才会意识到它们的重要性,所以下问开始就直接拿源码开刀了。

需要强调的是,本书联合了现有的资料,所以每一问虽短,但要理解和掌握还需要多看多想多总结。资料可以有所区分,但“坏”资料不一定就不包含关键或重要的信息。

Legacy驱动阶段

Sfilter系列

问6:DriverEntry和DriverUnload是干嘛的?(2009-04-20)

WDK把sfilter作为一个简单的legacy驱动例子,一看洋洋洒洒六千多行的源码,对于初学者来说可能有点无从下手的感觉。这里先将代码分三段:定义、声明、函数定义。我讲sfilter主要是沿着它的脉络把驱动编程中的一些东西展现给你,所以先不分析sfilter中的数据结构定义,声明没什么好讲的。这样内容就“少”了很多。本书开篇就提过DriverEntry 例程,这个系列就从DriverEntry开始吧。

驱动的DriverEntry例程的作用和它的名字一样,为驱动入口。驱动相关的初始化工作都要在这里完成。比如初始化全局变量、创建CDO、初始化分发例程和fastio例程、绑定设备等等。它在驱动程序的整个运行期间只被调用一次。sfilter默认绑定所有的现有设备和将来动态挂载的设备,所以假如你的需求与这个默认不冲突,你需要修改的地方不多。如果你想要手动指定驱动要绑定哪个设备的话你就需要参考filespy的例子了。现在简单说下,就是要自定义一个IOCTL代码,然后在用户模式下的某个应用程序中将该IOCTL发给驱动的CDO;在驱动中定义处理该IOCTL的程序,实际的绑定设备和断开设备操作都由该程序完成。SfFsNotification这个例程就是在设备挂载或断开的时候被文件系统自动调用来相应地绑定或断开该设备的。

DriverUnload例程自然是对应DriverEntry的,它主要完成驱动相关的卸载工作。在开发过程中,这个例程很有用,但不推荐在产品中提供此例程。

总结:

完了?完了。这里不是主战场。

问7:SfCreate(2009-04-20)

驱动里面把create/open操作都集中用IRP_MJ_CREATE表示。在文件系统驱动中,文件对象FileObject这个东西含义十分广泛,而不单单代表文件。所以不要以为你截获的所有IRP_MJ_CREATE的目标都是文件。

先来看看IRP_MJ_CREATE吧。

WDK中说:当一个新文件或新目录正被创建时,或者当一个现有的文件、设备、目录或卷正被打开时,I/O管理器就会发出IRP_MJ_CREATE请求。也就是说,假如你的SfCreate 被调用,那代表以上这些操作正要发生,但还没发生。拿透明加解密来说,这可能就是我所关注的文件正被创建或者正被打开。是可能,不是一定。在操作系统运行期间,create请求太多了,在你打开或创建文件的同时,其他进程都可能会针对不同目标发出多个create。你可以拿filemon或者filespy(两个用于查看IRP的工具)看看,在你不进行任何操作的情况下,随时都可能有各种各样的create及其他信息涌现。此时根据设计目标的不同,不同驱动基本上可能会有两种处理方法:1、在这里就判断出目标是否为要加密的文件;2、在完成例程中

判断。后者比较简单,就是自己发个IRP下去向文件系统驱动查询目标的名,再判断是否是要加密的文件。听起来似乎很不可思议!你确实可以自己发IRP来满足各种各样的需求。但权利和义务是并存的,暂略。理解过程可能漫长,但其实这些动作实现起来就几行代码的事。前者相对较难,就是自己要像文件系统一样把这个名给“制造”出来。Filemon和Filespy 源码里都有这个制造过程,暂略。好了,得出了名,你就知道它是什么东西了。比如c:\1.txt,c:\,c:\Documents and settings。当然,内核模式下卷名不会是c:,这里方便理解。拿到名了,也判断出是我要加密的文件了,那就想个办法把相关信息保存起来,以便我需要用到它时能找到。你可能会问,既然我只加密,那我在SfWrite里面判断是要加密的文件,则进行加密不就行了?不行。这个方法不一定有效,而且很容易“跟丢”了你要跟踪的文件。一些信息只被保证在create中是有效的。

因此create是个非常重要的dispatch routine。你需要在这里找出你要跟踪的文件;假如目标文件是你要跟踪的文件,则将必要信息保存起来,以启动跟踪;或许有的需求会让你做进程访问控制,那么你就需要在这里做处理,单单在read或write等例程中做控制就晚了。来看看sfilter例子里面的create都做了哪些事情。

if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {

Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;

Irp->https://www.doczj.com/doc/0918160974.html,rmation = 0;

IoCompleteRequest( Irp, IO_NO_INCREMENT );

return STATUS_INVALID_DEVICE_REQUEST;

}

这是一个比较典型的手法:直接完成IRP。由于sfilter并没有提供手动配置sfilter驱动环境的功能,所以一般情况下不可能有以sfilter的CDO为目标的IRP。这段代码的意思是,假如IRP的目标为sfilter的CDO,则返回的状态为STATUS_INVALID_DEVICE_REQUEST,附加信息为0。

IoSkipCurrentIrpStackLocation( Irp );

return IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->AttachedToDeviceObject, Irp );

这句可以参考SfPassThrough例程,即不做处理,直接下发给驱动栈中的下一个驱动,且不关心这个IRP的完成状态。

这里要用到一个常用的小技巧:设置事件。这里是直接把sfilter例子里面的代发粘贴出来,实际使用时需要参考WDK。按照几条语句的调用顺序如下排列:

KEVENT waitEvent;//声明一个事件

KeInitializeEvent( &waitEvent, NotificationEvent, FALSE );//将事件初始化为通知事件KeSetEvent(event, IO_NO_INCREMENT, FALSE);//给事件发信号

KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, NULL);//等待这个事件事件初始化的时候比较重要,你必须决定你要用这个事件做什么,例子中利用它作为通知事件以便在条件合适的时候(即在符合某一条件时调用KeSetEvent来给事件发信号)执行KeWaitForSingleObject之后的代码。还有一种是同步事件。作用同名,用于同步。

以下代码主要作用是设置完成例程。新出现的两个调用可以参考WDK,非常详细。IoCopyCurrentIrpStackLocationToNext( Irp );

IoSetCompletionRoutine(

Irp,

SfCreateCompletion,

&waitEvent,

TRUE,

TRUE,

TRUE );

status = IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->AttachedToDeviceObject, Irp );

这里不要接着SfCreate往下看,应该转向SfCreateCompletion。

一旦SfCreateCompletion被调用,那就代表IRP_MJ_CREATE已经被文件系统驱动收到并处理过了。例子中的SfCreateCompletion只是给事件发个信号,没有其他处理。

然后回到

if (STATUS_PENDING == status) {

NTSTATUS localStatus = KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, NULL);

ASSERT(STATUS_SUCCESS == localStatus);

}

后面就比较好理解了,SfDisplayCreateFileName这里需要另外讲。

问8:SfDisplayCreateFileName(2009-04-23)

SfDisplayCreateFileName单独开问并不是为了讲它,关键是它内部得到名的处理过程。

先看看它涉及的数据结构

typedef struct _GET_NAME_CONTROL {

PCHAR allocatedBuffer;//字符型指针

CHAR smallBuffer[256];//字符型数组

} GET_NAME_CONTROL, *PGET_NAME_CONTROL;

typedef struct _OBJECT_NAME_INFORMATION {

UNICODE_STRING Name;

} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;

首先声明和初始化OBJECT_NAME_INFORMATION类型的结构体作为容器,然后调用对象管理器例程ObQueryNameString试图得到对象名假如得不到名则得到对象所在的设备名。若返回的状态为STATUS_BUFFER_OVERFLOW(编程过程中你会遇到很多状态值,这个意为“容器的容量不足”),则分配更大的内存重新获得名。后面需要读者拿笔在纸上记录下每行代码执行后的结果,你必须要在这个过程中体会如何操作文件名。不自己体会,看到更复杂的文件名本身的操作时,你就会更加困惑。关于内存申请,也需多看资料多体会。尤其是从分页内存池和非分页内存池中分配的内存的不同之处。其他非fastio系列的例程要么非常简单易懂,要么其他资料上讲解得非常详细这里就不再赘述了。

总结:

这一节内容非常少,但需要做的功课量不少,我有理由相信你只盯着那些代码看是看不出结果的,所以在目前还无法完成一个可调试驱动的情况下,你得拿纸笔演算。要融入驱动开发这个环境,不太容易,但问题本就那么多,今天解决一个未来就少一个。下一节开讲fastio系列的例程。

问9:fastio系列例程(2009-04-28)

Fastio系列例程的资料不多,这里我翻译了一篇osr论坛上的技术文档。

NT中以I/O Request Packet (IRP)作为与驱动通信的基础– IRP的优点是它封装了一个操作所必需的上下文并允许驱动从对驱动公开的详细资料中提取有用信息。

虽然这种方式非常全面且可扩展–允许清晰的分层的Windows NT设备体系,但是要快速满足这个请求的一系列操作的消耗也十分明显。在这种情形下,创建一个IRP的消耗甚至能支配整个操作的消耗,而这会在性能的临界区域使系统“慢下来”。因此,引入了fast I/O 的概念。这种I/O方式被某些文件系统驱动使用,比如NTFS, HPFS, FAT和CDFS也包括用于WinSock的AFD传输驱动。

任何驱动都能注册一组fast I/O入口点但是它们的使用一般都极其有限–只有满足合适的条件时一个fast I/O入口点才会被调用。比如,读和写fast I/O入口点只在文件的信息由缓存管理器维护时才会被调用。这些限制我们后面会加以描述。

当然fast I/O的最大问题就是其可利用的说明文档非常少–即使是file system development kit也没有提供有关fast I/O如何工作或者如何使用它的介绍。本文将提供对fast I/O基本原理的介绍,然后是对各种fast I/O调用的摘要性描述,最后是如何利用这个接口来改进你的驱动的性能。

基本原理

Fast I/O看起来像是一种快捷的方式–许多I/O操作重复地对同样的数据执行。例如,与大多数现代的操作系统一样,Windows NT将文件系统缓存与虚拟内存系统结合在一起–把系统内存当做文件系统信息的一个巨大的缓存来使用。这样的文件系统极其有效且推进了系统的真实性能和感知性能。

这样结合的第二个原因是Windows NT对内存映射的(memory-mapped)文件的支持。支持读/写访问和内存映射的文件访问同一数据,需要高消耗(因而低性能),缓存器一致性方案或者NT的方案–所有的数据都存储在虚拟内存系统中。这确保数据总是一致的–即使是两个不同的程序用不同的访问技术访问同一数据。

这个紧密的结合意味着读和写操作都能由被缓存的数据满足。在性能的探究中, 这个事实使得读和写操作可以通过简单地调用那些能将数据从VM缓存移动到用户的内存中或反向移动的例程来实现。由于操作能被同步地满足且不需要调用lower驱动,这就避免了分配IRP的消耗。这就是fast I/O操作实现的基本原理。

Fastio的入口点包含于ntddk.h 文件中的FAST_IO_DISPATCH结构中。该结构的第一个元素描述这个结构的大小,这使得新元素能直接被兼容到这个结构中。

I/O管理器和Fast I/O

需要时I/O管理器负责调用fast I/O入口点。它所使用的方式几乎都是直截了当的– fast I/O返回TRUE或者FALSE来指示fast I/O操作是否已被完成。如果没有被完成或fast I/O 不可用,则I/O管理器会创建一个IRP并发送给top level驱动。FAST_IO_DISPA TCH结构

中的最后三个入口点不属于这个模型。它们为I/O管理器提供其他服务。后面会讨论它们。FastIoCheckIfPossible

仅能被文件系统当做普通的文件系统运行时库的一部分(FsRtl 系列例程)来使用。其原型为:

typedef BOOLEAN (*PFAST_IO_CHECK_IF_POSSIBLE) (

IN struct _FILE_OBJECT *FileObject,

IN PLARGE_INTEGER FileOffset,

IN ULONG Length,

IN BOOLEAN Wait,

IN ULONG LockKey,

IN BOOLEAN CheckForReadOperation,

OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject

);

此例程仅用于读和写操作。同样的,当使用文件系统缓存管理实现时,它被FsRtl库中提供的一般fast I/O例程调用来允许文件系统探测读或写是否(依赖于CheckForReadOperation 参数的Boolean值)能由文件系统缓存来满足。注意此调用的参数极其地接近读和写fast I/O入口点的相应参数–除了这个例程没有任何与之关联的数据buffer。

FastIoRead和FastIoWrite

如果一个文件关联有有效的被缓存的数据,那么当对其发出一次读请求时,I/O管理器就会调用这个例程。其原型为:

typedef BOOLEAN (*PFAST_IO_READ) (

IN struct _FILE_OBJECT *FileObject,

IN PLARGE_INTEGER FileOffset,

IN ULONG Length,

IN BOOLEAN Wait,

IN ULONG LockKey,

OUT PVOID Buffer,

OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject

);

像先前讲到的,此调用的基本参数与FastIoCheckIfPossible使用的参数极其相近。所有这些入口点都被保证传入的参数值有效,例如,buffer对调用线程的上下文中的读有效且可用。fast I/O例程能做以下两种事情: 完成本次操作,设置IoStatus 域来指示本次操作的结果并返回TRUE 给I/O管理器。如果是这种情形,I/O管理器将完成这个I/O操作。例程也可以返回FALSE,此时I/O管理器将会简单地创建一个IRP并调用标准的分发入口点。

注意返回TRUE 不总是担保数据已经被转移了。例如,一次起始位置越过文件结尾的读会引起IoStatus.Results 被设置为STATUS_END_OF_FILE,数据不会被复制。而一次起始位置和结束位置之间包含文件结尾的读将会令其返回TRUE,IoStatus.Results 同样会被设置为STA TUS_END_OF_FILE,但这一次所有该文件中剩余的数据会被复制到buffer中,同时超出的部分不会被复制。

类似地,返回FALSE 不总是担保一些数据没有被转移。虽然可能性很小,一些数据可能已经被成功地复制了但此后会经历一个I/O错误,或者使buffer的内存变得不可访问。

以上任何一种情形都会产生一系列的二次影响。比如,当从缓存中读时,一些被读的数据可能当前不在缓存中。这会引起一个页错误,其结果是引起一个回调到文件系统中来满足这个页错误。

对于写的情形唯一的差别在Buffer 参数是IN而不是OUT。基本的处理模式非常类似。当然,一些错误的条件是不同的–媒体可能满了,需要分配新的页,等等。FastIoQueryBasicInfo and FastIoQueryStandardInfo

这些操作提供对标准NtQueryInformationFile API操作的支持,同时针对某些操作FastIoQueryBasicInfo 也结合NtCreateFile 一起使用。basic信息包括文件什么时候被创建,它什么时候被最后一次访问,什么时候被最后一次修改及一切这个文件的特殊的属性–比如隐藏文件,一个目录或其他适当的属性。standard 信息包括文件的分配大小,当前大小,到这个文件的连接的数量,一个指示该文件的删除是否已经被请求和一个指示器指示该文件是否是一个目录。

因为这些信息经常被缓存到内存中,fast I/O操作是完美的候选方案。的确,许多标准的效用基于例程探测此信息,这就意味着提高这些操作的性能将充分地增强感知效用的性能。比如文件管理器(winfile.exe).

这两个fast I/O例程有相同的接口:

typedef BOOLEAN (*PFAST_IO_QUERY_BASIC_INFO) (

IN struct _FILE_OBJECT *FileObject,

IN BOOLEAN Wait,

OUT PFILE_BASIC_INFORMATION Buffer,

OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject

);

Wait 参数指示调用者是否愿意阻塞来等待必需的信息。如果它被设为FALSE则这个调用必须要么完成而不等待,要么返回FALSE –此情形下能创建一个正常的包含整个操作必需的上下文的IRP。值得注意的是在NT 3.51中这两个入口点都没有在当Wait 被设置为FALSE时调用。

一旦调用,如果FileObject 是首次被打开,那么这些例程会看它们为这个文件存储的信息。这个信息也能"on the fly"地重构–比如,最后一次访问时间被一些文件系统驱动设置为当前系统的时间。当然,设置这些值由文件系统实现决定。

FastIoLock, FastIoUnlockSingle, FastIoUnlockAll, and FastIoUnlockAllByKey 这些入口点用于控制关联一个特殊文件的锁状态。被这些调用控制的锁定(locking)是针对单一文件的字节范围(byte range)锁定。因此能锁住一个文件的多个字节范围。虽然不是必需的,标准NT文件系统利用文件系统运行时包(FsRtl 系列例程)的服务,提供一个共有的代码基础来检验能否准予一个被请求的锁并存储关于目前保存在文件上的锁范围的信息。锁状态能通过NT API的调用NtLockFile 和NtUnlockFile来控制。

Windows NT中的锁有两种–独占的锁,即被锁住的字节范围是因为修改而锁的;共享的锁,即被锁住的字节范围是因为读而锁的。针对重叠的字节范围的多重共享锁能被准予,然后其中的任何一个都会被存储直到后来它被释放。关于任何一个锁的各种信息都会被存储以便其能为后来的快速访问所获得。FastIoLock 的接口为:

typedef BOOLEAN (*PFAST_IO_LOCK) (

IN struct _FILE_OBJECT *FileObject,

IN PLARGE_INTEGER FileOffset,

IN PLARGE_INTEGER Length,

PEPROCESS ProcessId,

ULONG Key,

BOOLEAN FailImmediately,

BOOLEAN ExclusiveLock,

OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject

);

对应FileOffest 和Length 参数的字节范围由调用者锁定。ProcessId 提供信息来识别能解除这个锁的进程–允许后来的清除,比如,当该进程退出时。Key 参数提供一个不透明的值,比如它能被用于为通过FastIoUnlockAllByKey调用的快速访问把多重锁结合在一起。FailImmediately 指示此调用应该被阻塞直到锁可用,或者应该直接返回失败。对于FsRtl 例程,FailImmediately 被忽略–假如锁不可用,就会返回FALSE给调用者。ExclusiveLock 参数指示此次锁请求是独占(写)访问还是共享(读)访问。

FastUnlockSingle 例程用于释放在文件的一部分上的字节范围锁定。

这个调用的原型为:

typedef BOOLEAN (*PFAST_IO_UNLOCK_SINGLE) (

IN struct _FILE_OBJECT *FileObject,

IN PLARGE_INTEGER FileOffset,

IN PLARGE_INTEGER Length,

PEPROCESS ProcessId,

ULONG Key,

OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject

);

对于大多数文件系统,除非文件已经关联Op Locks, 这个操作总是返回TRUE,因为即使字节锁范围不能被访问这个操作也已经完成了(虽然会是一个错误的状态)。既然同样的IRP 调用会产生同样的结果, 处理暂时被完成。

对于成功之后的解锁操作,FileOffset, Length, ProcessId和Key 都必须匹配一个现有的字节范围锁。否则,这个操作将被完成且将被返回给调用者的IoStatus 中会设置错误STA TUS_RANGE_NOT_LOCKED。FastIoUnlockAll 例程用于释放被进程控制的文件的所有关联的字节范围锁。这个函数的原型为:

typedef BOOLEAN (*PFAST_IO_UNLOCK_ALL) (

IN struct _FILE_OBJECT *FileObject,

PEPROCESS ProcessId,

OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject

);

此时,fast I/O例程查询特定文件的可用锁列表并删除一切给定ProcessId关联的锁,不论独占的还是共享的。当NtCloseFile 被调用时系统会使用这个函数,要么是因为一个程序关闭了它要么是一个进程被删除。

FastIoUnlockAllByKey 操作用于删除一组已经被调用者用一个特殊的key值逻辑地关联的字节范围锁。这个例程的原型如下:

typedef BOOLEAN (*PFAST_IO_UNLOCK_ALL_BY_KEY) (

IN struct _FILE_OBJECT *FileObject,

PVOID ProcessId,

ULONG Key,

OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject

);

这个调用主要是为方便文件服务器比如SRV。NT 3.51中的I/O管理器没有利用它。key 允许一个文件服务器把一个文件锁与一些远程客户端关联起来。既然它可能表示像这样的远程客户端,单独的ProcessId 就不够了。同样地,既然存在多重文件服务器,单独的Key 的使用可能引起其他文件服务器的文件锁的不正确释放。结合两者能确保正确的操作和允许远程系统锁定。

FastIoDeviceControl

此入口点用于支持NtDeviceIoControlFile 调用,该调用本质上是用来实现对内核常驻的驱动的私有通信通道。与其他Fast I/O例程一样, 如果此操作完成则返回TRUE,否则的话返回FALSE。若为FALSE,I/O管理器创建一个IRP并调用驱动的分发入口点。

其原型如下:

typedef BOOLEAN (*PFAST_IO_DEVICE_CONTROL) (

IN struct _FILE_OBJECT *FileObject,

IN BOOLEAN Wait,

IN PVOID InputBuffer OPTIONAL,

IN ULONG InputBufferLength,

OUT PVOID OutputBuffer OPTIONAL,

IN ULONG OutputBufferLength,

IN ULONG IoControlCode,

OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject

);

Wait 指示调用者是否愿意等待此操作完成,即使Wait被设置为TRUE,Fast I/O例程也可能返回FALSE。InputBuffer 从”可选的”角度来说它指向一个可选的且由潜在的有效的调用者提供的buffer,尽管Fast I/O例程实现能在InputBuffer缺失或其长度错误时指示一个错误(例如, STATUS_INV ALID_PARAMETER)而返回TRUE。OutputBuffer指向内存(可能有效) ,它依赖于IoControlCode 访问类型值。如果IoControlCode 指示访问类型为3,则OutputBuffer 无效且Fast I/O例程负责确认它。否则,此buffer在调用Fast I/O例程之前有效。这种方式与正常的IRP_MJ_DEVICE_CONTROL分发入口点一样。

Fast I/O例程实现完全依赖于被传给FastIoDeviceControl例程的IoControlCode值。尤其是这个入口点关联那些实现一个充足的私有通信接口的内核模式驱动。因此,没有一个NT 自身的文件系统真正地实现这个入口点,但是WinSock支持的驱动AFD频繁地使用这个接口来与底层的传输驱动通信。

因此,这些通信真正的模型完全依赖于Fast I/O 例程的驱动实现。AcquireFileForNtCreateSection and ReleaseFileForNtCreateSection

这两个例程与Fast I/O分发表中的其他入口不相同。它们用于解决某些围绕HPFS文件系统的锁定(作为NT自身的四个文件系统之一,只有HPFS真正地使用这两个例程). 此二者的原型一样,如下:

typedef VOID (*PFAST_IO_ACQUIRE_FILE) (

IN struct _FILE_OBJECT *FileObject

);

AcquireFileForNtCreateSection在从存储在文件系统中的一个文件映射到页之前被调用来确保一切驱动指定的锁定已经完成。ReleaseFileForNtCreateSection被调用来释放之前为文件映射操作获得的锁。

如果没有提供这些入口,I/O Manager会利用一个默认的机制来确保正确地同步文件映射操作。

FastIoDetachDevice

最后一个例程是最古怪的。ntddk.h文件提供了这个调用的一个线索–即当一个设备对象将要被删除时会调用它。我们发现这个调用在开发文件系统过滤驱动时极其有效,因为当底层的媒体被改变时移动式文件系统的设备对象都被破坏了。有时这会立即发生,但它也能在媒体已经被移除之后的几乎任何时间发生,这依赖于系统的什么部分仍然在缓存着其信息。

此调用的原型为:

typedef VOID (*PFAST_IO_DETACH_DEVICE) (

IN struct _DEVICE_OBJECT *SourceDevice,

IN struct _DEVICE_OBJECT *TargetDevice

);

根据我们的经验,移动式媒体文件系统的一个过滤驱动必须能处理这个调用否则系统将停止。

[注意由此开始的fast I/O调用在原始文章中并没有被描述.] FastIoQueryNetworkOpenInfo

LanManager (CIFS)文件服务器的一个普通的操作是打开一个文件并重获其标准属性和基本属性。然后这个信息被组合并发送给远程客户端(LanManager/CIFS重定向器)以便它能为远程应用程序所用。

在NT 4.0之前, SRV需要多次传递一个IRP到底层的文件系统来提取相同的信息。从NT 4.0开始一个新的信息类型FileNetworkOpenInformation被添加到文件系统接口中,以便一个像SRV这样的网络文件服务器能以一个单一的操作提取这个信息。为促进这个处理的速度,这个fast I/O入口点也就被添加了进来。其原型如下:

typedef BOOLEAN (*PFAST_IO_QUERY_NETWORK_OPEN_INFO) (

IN struct _FILE_OBJECT *FileObject,

IN BOOLEAN Wait,

OUT struct _FILE_NETWORK_OPEN_INFORMA TION *Buffer,

OUT struct _IO_STATUS_BLOCK *IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject

);

与其他fast I/O例程一样, FileObject 表示在其上调用者正起作用的文件。Wait 参数指示调用者是否愿意阻塞。如果Wait为FALSE则这个例程不能被阻塞。然后调用者能使用标准的IRP方式来从文件系统中获得此信息。Buffer 参数指向数据应该被复制的位置,IoStatus 块指向标准的I/O状态信息。最后,DeviceObject 表示正被查询的文件系统实例。

既然是被SRV使用,这仅由物理文件系统实现。

FastIoAcquireForModWrite

这个调用被添加进来是为了允许在内存管理器的Modified Page Writer真实地执行I/O 之前的一个锁定文件的轮流机制。这个函数完全可选–但如果你不实现它,文件系统运行时库将使用文件对象的公有头中的ERESOURCE指针来确保正确地同步(因此你的文件系统必

windows驱动开发和调试环境搭建

Windows驱动开发和环境搭建 【文章标题】: Windows驱动开发和调试的环境设置 【文章作者】: haikerenwu 【使用工具】: VC6.0,VMware6.0.3,Windbg 【电脑配置】: 惠普笔记本xp sp3 (一)VMWare安装篇 VMWare的安装一路Next即可,关于其序列号,百度一下就能找到,虚拟机安装完成之后,需要安装操作系统,我在虚拟机中安装的是windows xp sp2系统。 点击“文件”----“新建”----“虚拟机” 进入新建虚拟机的向导,配置虚拟系统参数

选择虚拟系统文件的兼容格式(新手推荐选择默认选项) 按照默认设置继续点击下一步,选择好您需要的操作系统,此处我选择的是Windows XP Prefessional。 设置虚拟机名称和虚拟操作系统安装路径,我单独空出来一个F 盘,将虚拟机和虚拟操作系统全部装在该盘。

配置网络模式(推荐选择NA T,一般主机不用做任何的设置虚拟机就可以利用主机上网)。 配置虚拟磁盘的容量。在这里可以直接单击完成,来完成基本操作设置,磁盘默认空间是8GB,用户可以根据自己的实际使用情况来调整大小,也可以自定义分区。

操作完成之后,在“VM”菜单下有个“setting。。。”菜单,点击此菜单,在CD-ROM中选择合适的选项,我使用的是Use ISO image 选项,将我的xp sp2操作系统的ISO映像路径设置好,安装操作系统。点击ok之后,启动虚拟机,即开始安装操作系统,安装过程跟普通装机过程相同。安装完成之后,启动操作系统,然后在VM菜单下点击“Install VMWare Tools”,把虚拟操作系统的驱动装好。 (二)VMWare设置篇

wdm驱动开发之路

WDM驱动开发之路 写在前面:在专栏的前几期中,我们一起初步学习了vxd的开发技术。Vxd技术是很深奥的,不是一篇两篇文章能讲清楚,但你已经入了门,剩下的就要看你的修行了。多看书,多泡论坛(当然是上咱们的驱动开发网论坛了:->),多写程序…我的手不够用了。功到自然成嘛。不过话又说回来,vxd只是权宜之计,WDM才符合当今的潮流(程序员都是时髦人士,君不见先是VB、VC然后是asp、JSP、PHP,数也数不过来呀),Win9x寿终正寝时也就是vxd的末日,你不想随它而去吧(开个玩笑),那就随我来。 按笔者的想法,这篇文章写成连载形式,一次讲一个主题,并且必要时带着例子,让大伙step by step地把WDM驱动弄个透底,不想让大家觉得稀里糊涂,也不想让大家觉得白买杂志了。 今天我们一起讨论第一部分,了解篇。 (一)了解篇 WDM模型(Windows Driver Model)是微软公司为当前主流操作系统Windows98和Windows 2000的驱动程序设计的一种构架。它和传统的win3.x和win95使用的vxd的驱动是完全不同的体系结构。不过对于最终用户来说,WDM驱动程序在Windows98和Windows2000下的表现很相似。作为驱动开发人员来说,它在两者中有很多的不同。并且Windows98中的WDM只能算是Windowss2000中的WDM的一个了集。在Windows98中有一些驱动程序只能使用VXD来实现,如串行通讯驱动等。 要写驱动程序,首先要了解操作系统的结构。在WDM体系中,windows2000操作系统中是最标准的实现方式,Windows98则是部分兼容WDM结构。照微软的说法,Windows98和Windows2000 X86(Intel 架构)版本实现二进制码兼容(参见98DDK),Windows2000 x86版本与其它CPU平台版本实现源码级兼容(因为Windows 2000是基本NT相似的结构,最底层是硬件抽象层HAL,所有我们相信它们之间能源码级兼容)。但实际上,Windows2000的WDM实现中有很多例程在Windows98中没有实现,一旦试图加载这样的WDM驱动程序到Windows98中,则不能正常加载,当然我们也有办法实现它,那就是利用“桩”技术。具体可参见Walter Oney写的《Programming the Microsoft Windows Driver Model》一书。我们首先来看看Windows 2000的系统结构,然后再来看看Windows 98的。 图一是Windows 2000的系统结构图。从图中我们可以看出:整个系统被分为两个态,用户态和核心态。 从图中可以明显看出I/O操作最后是怎样作用到硬件上的。用户态应用程序对Windows 子系统进行win32 API调用,这个调用由系统服务接口作用到I/O管理器(严格地说,在Windows 系统中不存在I/O管理器这样的独立模块,这个只是为了方便叙述而将各种核心功能调用的集合称作I/O管理器,业界人士都这样称呼这个部分),I/O管理器进行必要的参数匹配和操作安全性检查,然后由这个请求构造出合适的IRP(IO Request Package,I/O请求包),并把此IRP传给驱动程序。简单情况下,驱动程序直接执行这个请求包,并与硬件打交道,从而完成I/O请求工作,最后由I/O管理器将执行结果返回给用户态程序。但在WDM体系结构中,大部分实行分层处理。即在图中“设备驱动“这部分,分成了若干层,典型地分成高层驱动程序、中间层驱动程序、底层驱动程序。每层驱动再把I/O请求划分成更简单的请求,以传给更下层的驱动执行。以文件系统驱动为例,最高层驱动只知道文件如何在磁盘上表示,但不知到怎样得到数据。最低层驱动程序只知道怎样从磁盘取出512B为单的数据块,但不知道文件怎样表示。举个更具体的生活例子。主人(最高层驱动)知道(并且需要)笔计本电脑,但不知道具体放在什么位置;而仆人(最底层驱动)却知道它放在具体什么地方,但

Windows驱动开发培训

Windows驱动开发培训 培训流程: 一、基础知识 在开始驱动开发之前,您应该知道操作系统原理以及驱动程序是如何在操作系统中进行工作的,了解这些基本原理将有助于您做出正确的设计决策并简化您的开发过程。 1、了解Windows操作系统构造\\ 可以链接进去 2、安装WDK,参考相关文档,熟悉WDK的内容\\ 可以链接进去 二、Windows驱动开发\\ 可以链接进去 一、基础知识 在开始驱动开发之前,您应该知道操作系统原理以及驱动程序是如何在操作系统中进行工作的,了解这些基本原理将有助于您做出正确的设计决策并简化您的开发过程。 1、了解Windows操作系统构造 (1)培训目标 深入了解Windows操作系统的系统结构以及工作原理 (2)培训内容 阅读书籍《深入解析Windows操作系统》的第3、4、6、7、9章,重点关注第九章“I/O系统” (3)培训任务 ①掌握Windows操作系统的系统结构 ②理解ISR、IRP、IRQL、DCP等概念的含义 ③了解注册表的用法,掌握注册表数据的查看和修改方法 ④了解进程和线程的内部机理以及线程的调度策略 ⑤了解I/O系统的内容,理解I/O请求以及I/O处理过程 注:以上相关内容,请在一周内完成。

2、安装WDK,参考相关文档,熟悉WDK的内容 (1)培训目标 了解WDK的安装过程,熟悉WDK的编译环境,掌握如何使用WDK的相关帮助文档;了解WDM驱动程序的基本结构 (2)培训内容 ①.阅读文档\\10.151.131.12\book\windows\MSWDM.chm,掌握WDM驱动程序的基本结构以及基本的编程技术。 ②.参考WDK的帮助文档:WDK documentation ,了解WDK的基本内容 (3)培训任务 ①理解分层驱动结构的含义,掌握设备和驱动程序的层次结构 ②理解“驱动对象”和“设备对象”的概念 ③理解2个基本例程:DriverEntry 和addDevice ④了解IRP的结构以及IRP处理的流程 ⑤初步了解I/O的控制操作 注:以上相关内容,请在一周内完成。 二、Windows驱动开发 学习如何基于WDK进行驱动程序的开发 1、培训目标 (1)学会根据WDK开发一个基本的Windows驱动程序和测试程序 (2)学会利用不同的IOCTL方式在内核模式和用户模式之间进行通讯 (3)学会如何在内核模式下和用户模式下访问注册表 (4)利用WinDbg跟踪程序,学会使用WinDbg进行调试 2、培训内容 (1)阅读\src\general\ioctl中的示例代码 (2)build并运行应用程序和驱动程序

Android驱动开发实例(控制LED灯)(精)

Android驱动例子(LED灯控制) 本例子,讲述在Android2.1上完全自已开发一个驱动去控制硬件口并写应用测试该驱动,通过这样一个例子,解析android下的驱动开发流程的应用调用流程,可以说是很好的入门引导 要达到的效果:通过Android的应用,调用驱动程序,在开发板上控制4个LED的亮灭。 一、硬件原理 如上图,通过4个IO口控制这LED,低电平LED亮, 这4个IO口分别是GPM1, GPM2, GPM3, GPM4, 二、驱动程序 1、在kernel文件夹下的driver目录,新键驱动文件夹 # cd kernel_Android_2.6.28.6/drivers 进到开发板的kernel目录,建驱动文件夹 #mkdir ledtest

2、在/driver/ledtest目录下,新建leddriver.c ,leddriver.h , Kconfig, Makefile 等4个文件leddriver.c leddriver.c 1. #include 2. #include 3. #include 4. #include/* For __init/__exit/... */ 5. #include 6. #include 7. #include 8. #include 9. #include 10. #include 11. #include 12. #include 13. #include 14. #include 15. #include 16. #include 17. #include//for register_chrdev( 18. #include 19. #include 20. #include"leddriver.h" 21. #include/* For MODULE_ALIAS_MISCDEV 22. (WATCHDOG_MINOR */ 23. #include/* For the watchdog specific items */ 24. #include/* For file operations */ 25. #define Viberator_MAJOR 97 //?÷éè±?o? 26. #define SCULL_NR_DEVS 4 27. #define SCULL_QUANTUM 4000 28. #define SCULL_QSET 1000 29. //---do as the GIO driver

android系统开发--HAL层开发基础

android系统开发--HAL层开发基础 Android HAL层,即硬件抽象层,是Google响应厂家“希望不公开源码”的要求推出的新概念 1,源代码和目标位置 源代码:/hardware/libhardware目录,该目录的目录结构如下: /hardware/libhardware/hardware.c编译成libhardware.so,目标位置为/system/lib目录 /hardware/libhardware/include/hardware目录下包含如下头文件: hardware.h 通用硬件模块头文件 copybit.h copybit模块头文件 gralloc.h gralloc模块头文件 lights.h 背光模块头文件 overlay.h overlay模块头文件 qemud.h qemud模块头文件 sensors.h 传感器模块头文件 /hardware/libhardware/modules目录下定义了很多硬件模块 这些硬件模块都编译成xxx.xxx.so,目标位置为/system/lib/hw目录 2,HAL层的实现方式 JNI->通用硬件模块->硬件模块->内核驱动接口 具体一点:JNI->libhardware.so->xxx.xxx.so->kernel 具体来说:android frameworks中JNI调用/hardware/libhardware/hardware.c中定义的hw_get_module函数来获取硬件模块, 然后调用硬件模块中的方法,硬件模块中的方法直接调用内核接口完成相关功能 3,通用硬件模块(libhardware.so) (1)头文件为:/hardware/libhardware/include/hardware/hardware.h 头文件中主要定义了通用硬件模块结构体hw_module_t,声明了JNI调用的接口函数 hw_get_module hw_module_t定义如下: typedef struct hw_module_t { /** tag must be initialized to HARDWARE_MODULE_TAG */ uint32_t tag; /** major version number for the module */ uint16_t version_major; /** minor version number of the module */ uint16_t version_minor; /** Identifier of module */ const char *id; /** Name of this module */ const char *name;

Windows 内核技术与驱动开发笔记(完整版)

Windows 内核技术与驱动开发笔记 1.简述Driver Entry例程 动程序的某些全局初始化操作只能在第一次被装入时执行一次,而Driver Entry例程就是这个目的。 * Driver Entry是内核模式驱动程序主入口点常用的名字。 * Driver Entry的第一个参数是一个指针,指向一个刚被初始化的驱动程序对象,该对象就代表你的驱动程序。WDM驱动程序的Driver Entry例程应完成对这个对象的初始化并返回。非WDM驱动程序需要做大量额外的工作,它们必须探测自己的硬件,为硬件创建设备对象(用于代表硬件),配置并初始化硬件使其正常工作。 * Driver Entry的第二个参数是设备服务键的键名。这个串不是长期存在的(函数返回后可能消失)。如果以后想使用该串就必须先把它复制到安全的地方。 * 对于WDM驱动程序的Driver Entry例程,其主要工作是把各种函数指针填入驱动程序对象,这些指针为操作系统指明了驱动程序容器中各种例程的位置。 2.简述使用VC进行内核程序编译的步骤 编译方式是使用VC++进行编译 1.用VC新建工程。 2.将两个源文件Driver.h和Driver.cpp拷贝到工程目录中,并添加到工程中。 3.增加新的编译版本。 4.修改工程属性,选择“project | setting”将IterMediate file和Output file 都改为MyDriver_Check。 5.选择C/C++选项卡,将原有的Project Options内容全部删除替换成相关参数。 6.选择Link选项卡,将原有的Project Options内容删除替换成相关Link。 7.修改VC的lib目录和include的目录。 8.在VC中选择tools | options,在弹出的对话框中选择“Directories”选项卡,在“Show directories for”下拉菜单中选择“Include file”菜单。添加DDK的相关路径。 3.简述单机内核调试技术 答:1.下载和安装WinDbg能够调试windows内核模块的调试工具不多,其中一个选择是微软提供的WinDbg 下载WinDbg后直接双击安装包执行安装。 2.安装好虚拟机以后必须把这个虚拟机上的windows设置为调试执行。在被调试系统2000、2003或是xp的情况下打开虚拟机中的windows系统盘。 3.将boot.ini文件最后一行复制一下,并加上新的参数使之以调试的方法启动。重启系统,在启动时就可以看到菜单,可以进入正常windows xp,也可以进入Debug模式的windows xp。 4.设置VMware管道虚拟串口。调试机与被调试机用串口相连,但是有被调试机是虚拟机的情况下,就不可能用真正的串口连接了,但是可以在虚拟机上生成一个用管道虚拟机的串口,从而可以继续内核调试。 4.请画出Windows架构简图

从零开始搭建Linux驱动开发环境

参考: 韦东山视频第10课第一节内核启动流程分析之编译体验 第11课第三节构建根文件系统之busybox 第11课第四节构建根文件系统之构建根文件系统韦东山书籍《嵌入式linux应用开发完全手册》 其他《linux设备驱动程序》第三版 平台: JZ2440、mini2440或TQ2440 交叉网线和miniUSB PC机(windows系统和Vmware下的ubuntu12.04) 一、交叉编译环境的选型 具体的安装交叉编译工具,网上很多资料都有,我的那篇《arm-linux- gcc交叉环境相关知识》也有介绍,这里我只是想提示大家:构建跟文件系统中所用到的lib库一定要是本系统Ubuntu中的交叉编译环境arm-linux- gcc中的。即如果电脑ubuntu中的交叉编译环境为arm-linux-

二、主机、开发板和虚拟机要三者互通 w IP v2.0》一文中有详细的操作步骤,不再赘述。 linux 2.6.22.6_jz2440.patch组合而来,具体操作: 1. 解压缩内核和其补丁包 tar xjvf linux-2.6.22.6.tar.bz2 # 解压内核 tar xjvf linux-2.6.22.6_jz2440.tar.bz2 # 解压补丁

cd linux_2.6.22.6 patch –p1 < ../linux-2.6.22.6_jz2440.patch 3. 配置 在内核目录下执行make 2410_defconfig生成配置菜单,至于怎么配置,《嵌入式linux应用开发完全手册》有详细介绍。 4. 生成uImage make uImage 四、移植busybox 在我们的根文件系统中的/bin和/sbin目录下有各种命令的应用程序,而这些程序在嵌入式系统中都是通过busybox来构建的,每一个命令实际上都是一个指向bu sybox的链接,busybox通过传入的参数来决定进行何种命令操作。 1)配置busybox 解压busybox-1.7.0,然后进入该目录,使用make menuconfig进行配置。这里我们这配置两项 一是在编译选项选择动态库编译,当然你也可以选择静态,不过那样构建的根文件系统会比动态编译的的大。 ->Busybox Settings ->Build Options

Windows驱动开发入门

接触windows驱动开发有一个月了,感觉Windows驱动编程并不像传说中的那么神秘。为了更好地为以后的学习打下基础,记录下来这些学习心得,也为像跟我一样致力于驱动开发却苦于没有门路的菜鸟朋友们抛个砖,引个玉。 我的开发环境:Windows xp 主机+ VMW ARE虚拟机(windows 2003 server系统)。编译环境:WinDDK6001.18002。代码编辑工具:SourceInsight。IDE:VS2005/VC6.0。调试工具:WinDBG,DbgView.exe, SRVINSTW.EXE 上面所有工具均来自互联网。 对于初学者,DbgView.exe和SRVINSTW.EXE是非常简单有用的两个工具,一定要装上。前者用于查看日志信息,后者用于加载驱动。 下面从最简单的helloworld说起吧。Follow me。 驱动程序的入口函数叫做DriverEntry(PDRIVER_OBJECT pDriverObj,PUNICODE_STRING pRegisgryString)。两个参数,一个是驱动对象,代表该驱动程序;另一个跟注册表相关,是驱动程序在注册表中的服务名,暂时不用管它。DriverEntry 类似于C语言中的main函数。它跟main的差别就是,main完全按照顺序调用的方法执行,所有东西都按照程序员预先设定的顺序依次发生;而DriverEntry则有它自己的规则,程序员只需要填写各个子例程,至于何时调用,谁先调,由操作系统决定。我想这主要是因为驱动偏底层,而底层与硬件打交道,硬件很多都是通过中断来与操作系统通信,中断的话就比较随机了。但到了上层应用程序,我们是看不到中断的影子的。说到中断,驱动程序中可以人为添加软中断,__asm int 3或者Int_3();前者是32位操作系统用的,后者是64位用的。64位驱动不允许内嵌汇编。下面是我的一个helloworld的源码:

操作系统与驱动开发试题

河北科技大学硕士学位研究生 2014——2015学年第1学期 《操作系统与驱动开发》课程期末考试试卷 学院信息学院专业电路与系统姓名程莉学号 2201414007 题号一二三四五六总分 得分 一.单项选择题(每小题1分,共10分) 1.操作系统的 D 管理部分负责对进程进行调度。 A.主存储器 B.控制器 C.运算器 D.处理机 2.分时操作系统通常采用 B 策略为用户服务。 A.可靠性和灵活性 B.时间片轮转 C.时间片加权分配 D.短作业优先 3.很好地解决了“零头”问题的存储管理方法是 A 。 A 页式存储管理 B 段式存储管理 C 多重分区管理 D 可变式分区管理 4.用WAIT、SIGNAL操作管理临界区时,信号量的初值应定义为 B 。 A.-1 B.0 C.1 D.任意值 5.在进程管理中,当 C 时,进程从阻塞状态变为就绪状态。 A.进程被进程调度程序选中 B.等待某一事件 C.等待的事件发生 D.时间片用完 6.某系统中有3个并发进程,都需要同类资源4个,试问该系统不会发生死锁的最少资源数 B 。 A.9 B.10 C.11 D.12 7.虚拟存储器管理系统的基础是程序的 B 理论。 A.全局性 B.局部性 C. 动态性 D.虚拟性 8.从用户的角度看,引入文件系统的主要目的是 D A.实现虚拟存储 B.保存系统文档

C.保存用户和系统文档 D.实现对文件的按名存取 9.操作系统中采用多道程序设计技术提高CPU和外部设备的 A A.利用率 B.可靠性 C.稳定性 D.兼容性 10.缓冲技术中缓冲池在 C 中。 A.主存 B. 外存 C. ROM D. 时间片轮转 二.填空(每空0.5分,共15分)。 11.进程存在的唯一标志是PCB 。 12.通常进程实体是由程序块、进程控制块和数据块三部分组成。 13.磁盘访问时间由寻道时间、旋转延迟时间和传输时间组成。 14.作业调度是从后备作业队列中选一些作业,为它们分配资源,并为它们创建进程。 15.文件的物理组织有顺序、链接和索引。 16.若一个进程已经进入临界区,则其它欲要进入临界区的进程必须___等待____。 17.信号量的物理意义是,当信号量值大于零时其值表示可分配资源的个数;当信号 量值小于零时,其绝对值表示等待使用该资源的进程的个数。 18.静态重定位在程序装入时进行; 而动态重定位在程序运行时进行。 19.分区管理中采用“最佳适应”分配算法时,宜把空闲区按长度递增次序登记在空闲 区表中。 20.所谓系统调用,就是用户在程序中调用操作系统所提供的一些子功能。 21.把逻辑地址映射为物理地址的工作称为地址映射。 22.设备管理中采用的数据结构有设备控制表、控制器控制表、通道控制表、 系统设备表等四种。 23.从资源管理(分配)的角度,I/O设备可分为独占设备、共享设备和虚 拟设备三种。 24.设备与控制器之间的接口信号主要包括数据、状态和控制。 25.DMA控制器由三部分组成,分别为主机与DMA控制器的接口、 DMA控制器与块设备的接 口和 I/O控制逻辑。 三.名词解释(每小题2.5分,共10分)。 26.虚拟存储器 答:虚拟存储器是指在具有层次结构存储器的计算机系统中,自动实现部分装入和部分替换功能,能从逻辑上为用户提供一个比物理贮存容量大得多,可寻址的“主存储器”。

驱动程序开发技术-过滤键盘驱动

《驱动程序开发技术》大作业 ——过滤键盘驱动 姓名:梁海杰 学号:2009441624 班级:计科普0902

摘要 Kbdclass.sys是键盘的类驱动,无论是USB键盘,还是PS/2键盘都要经过它的处理;在键盘类驱动之下,和实际硬件打交道的驱动叫做“端口驱动”,比如:i8042prt.sys是ps/2键盘的端口驱动,Kbdhid.sys是USB键盘的端口驱动。键盘中断导致键盘中断服务例程被执行,导致最终i8042prt的I8042KeyboardInterruptService被执行。在I8042KeyboardInterruptService中,从端口读取扫描码,放到一个KEYBOARD_INPUT_DATA 结构中。并把这个结构放到i8042prt的输入队列中。最后会调用内核api函数KeInsertQueueDpc。在这个调用中会调用上层KbdClass.sys中处理输入的回调函数KeyboardClassServiceCallback,取走i8042prt的输入数据队列里的数据。利用驱动分层机制,使用过滤驱动捕获键盘的扫描码并保存下来;应用程序定时访问驱动程序取回扫描码,转换成相应的按键名称并显示;通过应用程序设定按键映射,应用程序将指令传送给驱动程序,以实现将指定的按键消息转换成其他按键。 关键词:过滤键盘;驱动分层;映射;扫描码

过滤键盘驱动 一、主要设计思路 利用驱动分层机制,使用过滤驱动捕获键盘的扫描码并保存下来;应用程序定时访问驱动程序取回扫描码,转换成相应的按键名称并显示;通过应用程序设定按键映射,应用程序将指令传送给驱动程序,以实现将指定的按键消息转换成其他按键。 键盘过滤驱动是工作在异步模式下的。系统为了得到一个按键操作,首先要发送一个IRP_MJ_READ消息到驱动的设备栈,驱动收到这个IRP后,会一直保持这个IRP为未确定(pending)态,因为当时并没有按键操作。直到一个键被真正的按下,驱动此时就会立刻完成这个IRP,并将刚按下的键的相关数据做为该IRP的返回值。在该IRP带着对应的数据返回后,操作系统将这些值传递给对应的事件系统来处理,然后系统紧接着又会立刻发送一个IRP_MJ_READ请求,等待下次的按键操作,重复以上的步骤。 为了实现截获键盘消息,需要在过滤驱动程序中创建一个挂接到物理键盘设备上层的过滤驱动设备。系统发送的IRP_MJ_READ消息会首先到达过滤驱动设备,这样就可以有机会给IRP_MJ_READ设置指定的完成例程,然后将消息下传给物理键盘设备。当有按键动作发生时,IRP_MJ_READ消息在完成后就会调用指定的完成例程,这时就可以在完成例程中读出键盘动作的内容,或者修改这些信息,以实现按键的映射。

行为驱动开发

行为驱动开发 行为驱动开发(简称BDD)是测试驱动开发的升级版。它是一套软件工程实践方法,能帮助研发团队更快地构建和交付更有价值和更高质量的软件产品。采用BDD思想编写的测试读起来更像规格说明书而不是单元测试,所以它是使用测试作为表达和验证行为的一种手段。基于这个特性,BDD也非常适合应用在需求分析中。 一、行为驱动开发的原则 1.聚焦交付业务价值。使用验收标准作为目标,帮助业务实现更实际的可交付的功能。 2.团队共同确定交付标准。业务分析人员,开发人员,测试人员与最终用户一起定义和指定功能。 3.拥抱变化。项目开始时不锁定需求,而是假设需求,从用户那里得到早期的反馈,对需求的理解将在项目的整个生命周期中演进和变更。 4.不仅仅编写自动化测试,而是编写可执行规范和底层规范。团队将验收标准转换为自动化的验收测试,更准确地说是转换为可执行规范。在编写任何代码之前,开发人员将考虑代码实际上应该做什么,并将其表示为底层的可执行规范。可执行规范是一种自动化测试,它演示和验证应用程序如何交付特定的业务需求。自动化测试作为构建过程的一部分运行,并在对应用程序进行更改时运行,进行验收测试和回归测试。 5.交付活文档,并使用活文档来支持后续维护工作。在项目结束后持续维护项目可执行规范。 二、行为驱动开发的优势 1.专注业务目标,避免工程师把工作量浪费在不提供业务价值的功能上,能够降低成本,减少浪费。

2.完整的可执行规范,可充当开发人员的辅助技术文档,更容易理解现有的代码库并进行更改。 3.全面的自动化验收测试和回归测试,不仅可以提升执行效率,也能降低手工测试的出错率,使得迭代速度更快更可靠。 三、行为驱动开发的缺陷 1. 需要多个角色高度参与和协作,涉众如果不愿意或不能参与对话和协作,或者等到项目结束后才给出反馈,就很难充分利用BDD的优点。 2.比较适用于敏捷开发,但不太适用于瀑布式开发。 3.对参与角色能力要求很高,尤其是测试团队,不仅需要精通业务,对业务目标清晰,而且对测试技术能力要求更高,如果编写的自动化测试很烂,会导致更高的测试维护成本。

Windows驱动程序开发环境配置

Windows驱动程序开发笔记 一、WDK与DDK环境 最新版的WDK 微软已经不提供下载了这里:https://https://www.doczj.com/doc/0918160974.html,/ 可以下并且这里有好多好东东! 不要走进一个误区:下最新版的就好,虽然最新版是Windows Driver Kit (WDK) 7_0_0,支持windows7,vista 2003 xp等但是它的意思是指在windows7操作系统下安装能编写针对windows xp vista的驱动程序, 但是不能在xp 2003环境下安装Windows Driver Kit (WDK) 7_0_0这个高版本,否则你在build的时候会有好多好多的问题. 上文build指:首先安装好WDK/DDK,然后进入"开始"->"所有程序"->"Windows Driver Kits"->"WDK XXXX.XXXX.X" ->"Windows XP"->"x86 Checked Build Environment"在弹出来的命令行窗口中输入"Build",让它自动生成所需要的库 如果你是要给xp下的开发环境还是老老实实的找针对xp的老版DDK吧,并且xp无WDK 版只有DDK版build自己的demo 有个常见问题: 'jvc' 不是内部或外部命令,也不是可运行的程序。 解决办法:去掉build路径中的空格。 二、下载 WDK 开发包的步骤 1、访问Microsoft Connect Web site站点 2、使用微软 Passport 账户登录站点 3、登录进入之后,点击站点目录链接 4、在左侧的类别列表中选择开发人员工具,在右侧打开的类别:开发人员工具目录中找到Windows Driver Kit (WDK) and Windows Driver Framework (WDF)并添加到您的控制面板中 5、添加该项完毕后,选择您的控制面板,就可以看到新添加进来的项了。 6、点击Windows Driver Kit (WDK) and Windows Driver Framework (WDF),看到下面有下载链接,OK,下载开始。下载后的文件名为: 6.1.6001.18002.081017-1400_wdksp-WDK18002SP_EN_DVD.iso将近600M大小。

嵌入式linux android驱动工程师 面试题总汇

嵌入式linux android驱动工程师面试题总汇 1. 嵌入式系统中断服务子程序(ISR) 收藏中断是嵌入式系统中重要的组成 部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字__interrupt。下面的代码就使用了__interrupt 关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。 __interrupt double compute_area (double radius) { double area = PI * radius * radius; printf(" Area = %f", area); return area; } 1). ISR 不能返回一个值。2). ISR 不能传递参数。3). 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。4). 与第三点一脉相承,printf()经常有重入和性能上的问题。 2.C语言中对位的操作,比如对a的第三位清0,第四位置1.本来应该会的,一犯晕写反了,以后注意! #define BIT3 (1<<3) #define BIT4 (1<<4) a &= ~BIT3; a |= BIT4; 3.考到volatile含义并举例:理解出错,举了很具体的例子,连程序都搬上去了,有些理解不深的没举出来…… volatile表示这个变量会被意想不到的改变,每次用他的时候都会小心的重新读取一遍,不适用寄存器保存的副本。 volatile表示直接存取原始地址 例: 并行设备的硬件寄存器(状态寄存器) 在多线程运行的时候共享变量也要时时更新 一个中断服务子程序中访问到的的非自动变量(不太清楚,正在查找资料ing……) 4.要求设置一绝对地址为0x67a9 的整型变量的值为0xaa66 当时我的写法:#define AA *(volatile unsigned long *)0xaa66 AA = 0x67a9; 答案:

WINDOWS驱动编程

WDM驱动程序开发之读写设备寄存器:KIoRange类 2009-11-09 14:05 WDM驱动程序开发之读写设备寄存器:KIoRange类收藏 KIoRange类: 一、Overview KIoRange类将一系列特殊的外围总线的地址映射到CPU总线的地址空间。CPU总线上的地址即可能在CPU的I/O空间,也可能在CPU的内存空间,这取决于平台和外围总线的控制方式。考虑到可移植性,所有对I/O周期(I/O cycle)进行译码的设备驱动程序必须用这个类对I/O的位置(location)进行正确的访问(access)。KIoRange是KPeripheralAddress类的派生类。 一旦映射关系建立起来,驱动程序就用KIoRange类的成员函数去控制设备的I/O寄存器。这个类提供了8位、16位和32位I/O访问控制的函数。这些函数是以内联(in-line)函数方式来使用的,它们调用系统内相应的宏来产生依赖于平台的代码。 对I/O位置(location)进行访问的另一种备选方案是创建一个KIoRegister 的实例。这要通过取得一个KIoRange对象的数组元素来实现。 为了访问一系列外围总线内存空间的地址,需要用KMemoryRange类。 二、Member Functions 1、KIoRange - Constructor (4 forms) 构造函数 【函数原型】 FORM 1: KIoRange( void ); FORM 2: (NTDDK Only) KIoRange( INTERFACE_TYPE IntfType, ULONG BusNumber , ULONGLONG BaseBusAddress, ULONG Count, BOOLEAN MapToSystemVirtual =TRUE ); FORM 3 (WDM): KIoRange( ULONGLONG CpuPhysicalAddress, BOOLEAN InCpuIoSpace, ULONG Count, BOOLEAN MapToSystemVirtual =TRUE

USB驱动开发

第17章USB设备驱动 USB设备驱动和PCI设备驱动是PC中最主要的两种设备驱动程序。与PCI协议相比,USB协议更复杂,涉及面较多。本章将介绍USB设备驱动开发。首先介绍USB协议,使读者对USB协议有个整体认识。然后介绍USB设备在WDM中的开发框架。由于操作系统的USB总线驱动程序提供了丰富的功能调用,因此开发USB驱动开发变得相对简单,只需要调用USB总线驱动接口。 17.1 USB总线协议 USB总线协议比PCI协议复杂的多,涉及USB物理层协议,又涉及USB传输层协议等。对于USB驱动程序开发者来说,不需要对USB协议的每个细节都很清楚。本节概要地介绍USB总线协议,并对驱动开发者需要了解的地方进行详细介绍。 17.1.1 USB设备简介 USB即通用串行总线(Universal Serial Bus),是一种支持即插即用的新型串行接口。也有人称之为“菊链(daisy-chaining)”,是因为在一条“线缆”上有链接127 个设备的能力。USB要比标准串行口快得多,其数据传输率可达每秒4Mb~12Mb(而老式的串行口最多是每秒115Kb)。除了具有较高的传输率外,它还能给外围设备提供支持。 需要注意的是,这不是一种新的总线标准,而是计算机系统连接外围设备(如键盘、鼠标、打印机等)的输入/输出接口标准。到现在为止,计算机系统连接外围设备的接口还没有统一的标准,例如,键盘的插口是圆的、连接打印机要用9针或25针的并行接口、鼠标则要用9针或25针的串行接口。USB能把这些不同的接口统一起来,仅用一个4针插头作为标准插头,如图17-1所示。通过这个标准插头,采用菊花链形式可以把所有的外设连接起来,并且不会损失带宽。USB正在取代当前PC上的串口和并口。

单片机驱动开发班

单片机驱动开发班 课程背景: 随着经济的发展,科技的突飞猛进,芯片技术也取得了飞速发展,这就使单片机技术在各种民用和工业测控等领域得到更为广泛应用。包括如今异常火爆的汽车电子中的车身控制、底盘控制、发动机控制、安全控制、娱乐系统等;包括传统的工业控制中的电机控制、温控系统、仪表设备、楼宇自控系统、数据采集系统等;包括计算机网络通信、数据传输、军用设备、航空航天等。单片机凭借其低成本、高性能的不可替代优势,已经成为微电脑控制的主力军。据统计,我国的单片机年需求量已达2亿片以上,且每年以大约15%的速度增长,发展迅速的单片机行业有着广阔的前景。 相比于发展迅猛的单片机行业,国内的单片机设计开发从业人员缺口很大。据统计,到2015年,我国单片机开发从业人员将达350万人,而目前的从业者只有大约一百五十万人,两百万的人才缺口正驱动大量人员加入这个庞大的群体。 学院优势: 硅谷芯微是深圳市硅谷龙科技有限公司教育产业下属专门从事IT实训的独立机构,深圳市硅谷龙科技有限公司始创于中国深圳,由侯工单片机工作室投资成立,致力于通过创新、高品质的课程体系和高效、实用的教育服务推动我国芯片级IT教育体系的建设。 深圳市硅谷龙科技有限公司致力于提供个性化、着眼于未来的教育服务,把国际先进且具有自主知识产权的案例全面应用于教育服务,在IT职业教育领域,硅谷龙以实用型的工程师人才培养理念作导引,以学生就业和职业生涯发展为指向,以成熟的IT开发经验为基础,通过完善、创新的课程体系以及全球化的企业合作为保障开展IT各类职业人才教育。硅谷龙在全国范围内建立实训基地,通过系统的实训,帮助学生提高职业素质及就业竞争力,并最终完成学员的就业服务。 面向行业及岗位: 面向行业:消费类电子、工控、汽车电子、监控电子等 主要岗位:单片机工程师、单片机硬件工程师、单片机技术支持工程师、单片机销售工程师、单片机驱动工程师。 教学目标:

《设备驱动程序开发技术》大作业

《设备驱动程序开发技术》 大作业 WDM驱动程序的开发流程和要点班级:计算机科学与技术1004

摘要 DWDM(Windows Driver Model)是Microsoft公司推出的一种符合Windows2k/XP下的内核模式驱动程序的分层体系结构的驱动程序模式。它源于 Windows NT的分层32位设备驱动程序模型,它支持更多的特性,如即插即用( PnP ,Plug and Play )、电源管理( PM ,Power Management )、Windows管理诊断( WMI ,Windows Management Instrumentation )和 NT 事件。它为Windows操作系统的设备驱动程序提供了统一的框架,在Windows平台上,WDM将成为主流的驱动模式。WDM是Windows98和Windows2000使用的新的驱动程序设计规范。使用WDM使得硬件驱动程序更加稳定,让操作系统对硬件更加有效地控制硬件。除了定义一个驱动程序与操作系统连接的标准接口以外,WDM也指明了驱动程序应该采用的更加模块化的设计。 关键词: WDM、驱动程序、操作系统

1 概述 WDM(Windows Driver Model)是Microsoft公司推出的一种符合Windows2k/XP下的内核模式驱动程序的分层体系结构的驱动程序模式。相对于以前的KDM、VXD来说,它的性能更高、系统之间移植更加方便。随着Microsoft的操作系统的不断升级,WDM已逐步取代了KDM、VXD,成为了Microsoft系统下驱动程序开发的主流。 WDM是通过一个128位的全局唯一标识符(GUID)实现驱动程序的识别。应用程序与WDM 驱动程序通信时,应用程序将每个用户请求形成I/O请求包(IRP)发送到驱动程序。驱动程序识别出IRP请求后指挥硬件执行相应操作。 2 WDM驱动模型 WDM模型为存在于Windows 98和Windows 2000操作系统中的设备驱动程序提供了一个参考框架。尽管对于最终用户来说这两个操作系统非常相似,但它们的内部工作却有很大不同。 Windows 2000概述 图1是以我的视点所看到的Windows 2000操作系统,该图着重了驱动程序开发者所关心的特征。软件要么执行在用户模式中,要么执行在内核模式中。当用户模式程序需要读取设备数据时,它就调用Win32 API函数,如ReadFile。Win32子系统模块(如KERNEL32.DLL)通过调用平台相关的系统服务接口实现该API,而平台相关的系统服务将调用内核模式支持例程。在ReadFile调用中,调用首先到达系统DLL(NTDLL.DLL)中的一个入口点,NtReadFile 函数。然后这个用户模式的NtReadFile函数接着调用系统服务接口,最后由系统服务接口调用内核模式中的服务例程,该例程同样名为NtReadFile。

Android移动应用开发习题答案

Android移动应用开发习题答案 单元1 Android开发环境搭建与模拟器创建 1.填空题 (1) 2008 (2) Linux、操作系统 (3) 应用程序层、应用程序框架层、核心类库、Linux内核 (4) Java C++/C (5) 应用框架 (6) 应用程序 (7) 模拟器、Android模拟器 (8) IntelliJ IDEA Android 2.选择题 (1)创建程序的过程中,填写Application Name表示()。 正确答案:A (2)Android操作系统的手机可以有以下几个方法进行软件安装()。(多选题)ABCD (3)Android操作系统主要的特点是:()。(多选题) 正确答案:ABC (4)以下对Android操作系统描述正确的是:()。(多选题) 正确答案:ABCD (5)以下哪些是Android Stuido的优势()。(多选题) 正确答案:ABCD (6)以下哪些是Genymotion模拟器的优势()。(多选题) 正确答案:ABCD 3.简答题 (1)Android的四层架构分别包括哪几层?分别起到什么作用? 参考答案: Linux内核层(Linux Kernel):基于Linux内核,内核为上层系统提供了系统服务。 核心类库层(Libraries):系统库基于C/C++语言实现,通过接口向应用程序框架层提

基于Android Studio的移动应用开发任务驱动教程 供编程接口。 应用框架层(Application Framework):为开发者提供了一系列的Java API,包括图形用户界面组件View、SQLite数据库相关的API、Service组件等。 应用程序层(Applications):包含了Android平台中各式各样的应用程序。 (2)简述Android开发环境安装的步骤。 参考答案: 下载并安装JDK,配置JDK的环境变量; 从Anroid官网上下载Android开发组件(包含Eclipse和Android SDK、ADT); 安装Android开发环境(包括配置Android SDK的环境变量、打开Eclipse通过菜单设定Android SDK路径)。 (3)简述Android Studio的优势。 参考答案:略。 (4)简述Genymotion模拟器的优势。 参考答案:略。 (5)简述Android应用程序创建和运行的步骤。 参考答案: 通过菜单创建Android应用程序,设置应用程序名、Android版本等基本信息。 单击菜单中的运行按钮可以直接运行Android项目(也可以通过菜单配置运行参数, 指定运行的模拟器)。 单元2 Android Studio的组成结构与基本操作 1.填空题 (1) res/layout (2) Activity、Bundle (3) XML (4) java代码文件 (5) AndroidManifest.xml (6) 打包成.apk文件 2.选择题 (1)如果需要创建一个字符串资源,需要将字符串放在res\values的哪个文件中?()B (2)新建的Android程序默认的布局方式是相对布局(RelativeLayout),该布局中包含一个()控件。 B (3)要让布局文件或者控件能够显示在界面上,必须要设置RelativeLayout和控件的

相关主题
文本预览
相关文档 最新文档