首 页 网络编程
网页制作 图形图象 操作系统 冲浪宝典
软件教学 认证考试

网络安全 网络办公 行业资讯 评测对比
您当前位置:站长天空 -> 图形图象-> 相关软件
解读Windows 2000/XP分层驱动模型-.NET教程,Windows开发
作者:网友供稿 点击:250
推荐
西部数码-全国虚拟主机10强!20余项虚拟主机管理功能,全国领先!第6代双线路虚拟主机,南北访问畅通无阻!可在线rar解压,自动数据恢复设置虚拟目录等.免费赠送访问统计,企业邮局.Cn域名注册10元/年,自助建站480元起,免费试用7天,满意再付款!P4主机租用799元/月.月付免压金
站内搜索
文章页数:[1] 
可扩展性是windows nt/2000/xp设计的目标之一,其分层驱动模型是可扩展性的最好体现。实现分层依赖于io管理器的两个重要的设计:1、windows中的任何一个驱动程序都被设计成client/server模式。对于客户端驱动,通过iogetdeviceobjectpointer之类的获取服务端驱动导出的device对象,通过io管理器的iocalldriver请求服务端的服务。iocalldriver实际上根据客户端的调用参数(通过irp)调用服务端的派遣入口(回调函数)接受客户端的请求。2、io管理器实现一个分层的数据结构,在device_object对象中保存某种关系,自动将请求irp发给设备栈中的最高的一个设备,由其决定如何处理,或是自身处理,或是向下传递,达到分层的目的。鉴于这种能力,分层驱动模型可以实现很多应用,如文件监控,加密,防病毒等等,由于pnp的引入,这种应用将更加广泛。实际上这种分层模型在windows nt/2000/xp中无处不在,不信的话,请执行如下命令看看:

    findstr /m ioattachdevice %systemroot%\system32\drivers\*.sys

    所有列出的driver几乎都可以看作分层驱动的例子。对于分层驱动的介绍几乎充斥着所有介绍windows驱动的任何一本书中。本文不想过多于重复这些内容,旨在从底层数据结构的实现上说明这种分层的实现。我们首先从device_object开始说明。下面是这个结构的部分定义:

    typedef struct declspec_align(memory_allocation_alignment) _device_object {
           .
           .
           .
        struct _driver_object *driverobject;
        struct _device_object *nextdevice;
        struct _device_object *attacheddevice;
           .
           .
           .
        pvoid deviceextension;
           .
           .
           .
        cchar stacksize;
           .
           .
           .
        struct _devobj_extension  *deviceobjectextension;
           .
           .
           .
    } device_object;

    成员driverobject是deviceobject对应的driver_object,通过这个对象,io管理器可以知道如何为这个设备提供服务(通过调用driverobject提供的dispatch入口)。通常一个driver_object可以为一至多个device_object提供服务,各个device服务不同的同一类型的物理或逻辑设备。她们也有可能扮演不同的角色,如对于pnp引入的pdo与fdo,下面我会详细介绍。正因为这样nextdevice成员即用于链接这些device_object。所以可以得到这样的结论,像前一句所描述的,对于同一个driver导出的pdo与fdo则通过nextdevice成员逻辑上建立关系。而对于attacheddevice成员对于legacy的driver(windows nt 4.0之前,在之后的版本中也可以正常使用),主要通过这一字段来实现本文开头提到的io管理器提供的第二项功能。如下示意图所示,attachedevice1附加至device1之上, 这种情况下attachdevice1与device1通常不是由同一个driver服务的,即他们没有通过nextdevice成员链接在一起的,这样我们可以通过书写另一个driver,通过附加一个device至一个已存在的设备上改变或监视这个设备的行为。当然这时候device1的attacheddevice成员即指向attachdevice1。而attacheddevice1并没有被任何设备附接过,所以其attacheddevice成员指向null。通过调用iocalldriver请求device1服务时,io管理器内部会调用iogetattacheddevice之类的,获得附接在device1之上的最高层设备。这里是attacheddevice1,而对于device2,即是attacheddevice3,如果没有附接任何设备,当然就是device1了。这样我们附接的设备就有机会执行相应的操作了。另外对于下图device1与device2的情况,很明显,device1位于device2之上,而这时就是我们开头介绍的第一种情况,device1通过iocalldriver请求device2,逻辑上建立一种关系(我们不能通过任何数据结构标识这种关系,通常这是驱动开发人员设计成的逻辑关系,对于windows 2000/xp上如何知道这种关系,当然最好的出入就是ddk文档了)。


    |-------------|____attachdevice1
    |   device1   |
    |-------------|


                                       ------attachdevice3
    |-------------|____attachdevice2---|
    |   device2   |
    |-------------|


    deviceextension成员通常是驱动开发人员自已定义的结构,其存储设备相关的内容,例如用于区别pdo与fdo等等。在调用iocreatedevice时指定其大小,io管理器分配sizeof(device_object)+deviceextensionsize的非分页内存用于设备对象,这样这两个结构在物理上是连续的,所以我们在一些文件系统驱动程序中经常看过volume_device_object这样的定义(紧随标准的device_object后即是专用的用于volume的定义,避免在dispatch入口每次都要引用这个成员)。

    stacksize指当前设备栈的设备个数(attacheddevice个数加上设备本身),用于io管理器分配irp时指定stack_location个数。

    上面的所有叙述即构成了windows nt 4.0之前的windows分层驱动模型。这也是遗留的(legacy)的分层驱动程序的主要工作思路。attacheddevice指出了attached了的设备,microsoft在windows 2000中的deviceobjectextension结构成员中引入了一个attachedto指出被当前设备attached的低层设备,这在windows nt是没有实现的。deviceobjectextension是一个很重要的结构,下面要介绍的支持pnp的wdm驱动的adddevice入口也在这儿。她是由系统定义的结构(区别于deviceextension)。对于driver_object也有个类似的称为driverextenion的结构,后者有一个clientdriverextension结构,由ioallocatedriverobjectextension分配,iogetdriverobjectextension获得。classpnp.sys即是通过这一方法,实现对disk.sys、tape.sys与cdrom.sys的管理;ndis.sys也通过这一方法实现各个miniport/im/protocol driver的管理。

     windows 2000及其以后的nt系列引入了wdm,支持pnp、power管理及wmi,为了让操作系统本身就对此支持,ntoskrnl.exe中导出了几个置有drvo_builtin_driver(ntddk.h中定义)标志的driver,分别为pnpmanager、wmixwdm及acpi_hal,后者是支持acpi的hal(这是在我windows xp professional台式机上的情况,不知道不支持acpi的机子啥模样,我的机子上ntoskrnl.exe还导出\filesystem\raw驱动用于文件系统的支持,我想这几个设备名可能会随机器配置及windows版本不同而不同,实际上我在我windows 2000 server sp0笔记本上ntoskrnl.exe生成如下四个设备:pnpmanager,pci_hal,wdm与raw,我底下的叙述都是基本我台式机上的,至于出现的一些不同我可能会另外指出,也有可能没有),前两个driver都是为了支持wdm而引入的。wmixwdm导出wmidatadevice用作wmi的支持,这与本文讨论分层驱动没有关系,以下我重点介绍pnpmanager。

     在介绍之前,我们来看一下devmgmt.msc“依连接排序设备”视图:

     tsu00(机器名,由pnpmanager实现的虚拟root总线枚举)
         |
         |+ advanced configuration and power interface(acpi) pc(ntoskrnl中的acpi_hal驱动实现)
              |
              |+micrsoft acpi-compliant system(由acpi.sys枚举)
                    |
                    |+pci bus(由pci.sys实现)
                         |
                         |+(连接的设备)

    这是我机子上的情况,pnpmanager是一个总线驱动程序(在ntoskrnl.exe内部实现,如果你有checked build的ntoskrnl.exe,你可以很容易的发现其由base\ntos\io\pnpmgr\pnpdd.c实现,看过sysinternals导出的windows xp source tree了吗?),她实现一个称为root的虚拟总线。所有legacy设备,都是连接至这个虚拟的总线上的,不信的话,你在devmgmt.msc的上面列出的草图上继续选“显示隐藏的设备看看”。从这种意义上看她要为每一个连接到她上面的设备建立一个pdo。对于这些pdo通常是以00000001开始的十六进制命名,如在我机子我实验某一时刻设备名一直至00000050,共80个pdo(在我的windows 2000 server的机子上并不是这样命名的,虽然也是基于十六进制的,但却是从挺大的一个数值开始的)。我非常喜欢随windows xp ddk一些发行的osr的devicetree,但为了更好的理解,还是以windbg作个实验吧:

    找出上面草图acpi_hal附接的由pnpmanager实现的pdo,通常这是pnpmanager实现的第一个pdo,即命名为00000001(如果你的pnpmanager生成的设备不是这样命名的,请使用!drvobj pnpmanager找出生成的对应的pdo,我的windows 2000 server sp0的笔记本上,pnpmanager的第一个pdo用于服务ess声卡,而并不是我原以为的pci_hal,你可能另需要使用!devstack或是下面要介绍的!devnoe命令,方法不详述):

    kd> !object \device\00000001
    object: 812b4410  type: (812b4048) device
        objectheader: 812b43f8
        handlecount: 0  pointercount: 5
        directory object: e10011b0  name: 00000001
    kd> !devobj 812b4410  
    device object (812b4410) is for:
     00000001 \driver\pnpmanager driverobject 812b4980
    current irp 00000000 refcount 0 type 00000004 flags 00001040
    dacl e1518a6c devext 812b44c8 devobjext 812b44d0 devnode 812b42b8 
    extensionflags (0000000000)  
    attacheddevice (upper) 812f6bb0 \driver\acpi_hal
    device queue is not busy.

    我们很容易通过上面的输出含用attacheddevice的一行发现连接至设备00000001的是acpi_hal,与我们devmgmt.msc上看到的一致。实际上attached至这一pdo的是由acpi_hal服务的一个称为fdo的设备。pdo与fdo本身在内部都仍是由device_object来表示的,正像前面提及的对于总线驱动开发人员通常使用deviceextension中的一个标志区分同个driver服务的设备是pdo还是fdo。fdo通常由driver的adddevice入口建立,建立后使用ioattachdevicetodevicestack附接至下层总线驱动提供的pdo,正像上面windbg中我们看到的一样。这个attached操作与我们开头讨论的legacy设备是一样的,同样是使用device_object的attacheddevice成员。

    你可能会困惹pnpmanager的adddevice入口应该是怎样实现的。我们知道pnpmanager实现称为root的总线驱动程序,既然是root,肯定也就不存在attached上谁提供的pdo。实际上你可以使用windbg看看其实现:在free build xp中,其只实现return status_success(xor eax,eax/ret)了。我在用checked build的时候,其只是对irql进行检查:rtlassert(kegetcurrentirql()<=apc_level)。

    当然对于acpi_hal或是pci总线,其adddevice与pnpmanager实现肯定不同,她们肯定要iocreatedevice进立fdo,attached至pnpmanager提供的pdo或是上层总线提供的pdo,实现层次关系。我们继续使用windbg验证我这种思路:

    kd> !drvobj \driver\acpi_hal
    driver object (812f6ce8) is for:
     \driver\acpi_hal
    driver extension list: (id , addr)

    device object list:
    812f6a90  812f6bb0 

    acpi_hal导出的两个设备哪个是pdo,哪个是fdo呢(你应该会明白至少有一个fdo吧)。我们知道pnp管理器在发现总线后,首先会调用总线驱动程序的adddevice入口,然后才会发各种各样的irp_mj_pnp的各种minorfunction指示总线驱动程序枚举总线,对连接至上面的设备各建立pdo等等。这儿我只是描述通常情况,对于如ddk中附带的toaster这样的虚拟总线,其枚举总线上的设备是通过应用程序发相应的ioctl来指示pdo的建立(通过ioinvalidatedevicerelations让pnp管理器发irp_mj_pnp)。当然就算是toaster实现的这样虚拟总线,及adddevice入口,即fdo总是先于pdo的建立。因为对于同一个driver_object服务的设备,其是通过driver_object的deviceobject形成单向链表,这个deviceobject指示链表头,由device_object的nextdevice联接成链表。而对于iocreatedevice建立的设备,后建立的设备,总是插入表头,而fdo基本上总是最先建立的,所以总是在表尾。有了这些分析,对于acpi_hal上面的输出812f6bb0即是fdo,而812f6a90则是pdo。ok,这样这个pdo,则又是上层acpi.sys实现的总线驱动程序的下层pdo了。

    kd> !devobj 812f6a90  
    device object (812f6a90) is for:
     00000052 \driver\acpi_hal driverobject 812f6ce8
    current irp 00000000 refcount 0 type 0000002a flags 00001040
    dacl e1518a6c devext 812f6b48 devobjext 812f6b60 devnode 812f63a8 
    extensionflags (0000000000)  
    attacheddevice (upper) 812ad960 \driver\acpi
    device queue is not busy.

    你看看上层是不是\driver\acpi(attacheddevice行)。而pci总线又是attached至acpi.sys实现的micrsoft acpi-compliant system上的。注意acpi可不像acpi_hal一样,只有一个pdo,而pci总线也不是第一个pdo。有了这些知识,我想你也应该可以比较容易的发现pci总线附接至哪个pdo吧。一个更简单的办法是使用!devstack命令dump pci总线的fdo了。

    kd> !drvobj pci
    driver object (812ef850) is for:
     \driver\pci
    driver extension list: (id , addr)

    device object list:
    812f39e8  812f3d58  812f4e40  812f4038
    812f0710  812f0908  812f0c58  812f14e8
    812f0e38  
    kd> !devstack 812f0e38  
      !devobj   !drvobj            !devext   objectname
    > 812f0e38  \driver\pci        812f0ef0  
      812dc8c0  \driver\acpi       812f5660  00000058
    !devnode 812f1008 :
      deviceinst is "acpi\pnp0a03\2&daba3ff&0"
      servicename is "pci"

    devstack命令实际上使用device_object的attacheddevice与存于deviceobjectextension(注意这儿是deviceobjectextension而不是deviceextension)结构成员中的attachedto来显示设备栈的。当然devstack命令还显示设备对应的device_node。device_node是为了支持pnp而引入的一个系统数据结构,完整的device_node定义是非常复杂且非常庞大的,我就不列出来了,几个重要的成员如sibling(兄弟device_node),child(子device_node),parent(父device_node),设备状态pnp_devnode_state,资源使用情况cm_resource_list,接口类型interface_type,设备标识、服务名servicename等等。

    从我的提示devicenode含有sibling、child、parent等成员,我们很容易想到系统可能会将所有devicenode组成一个树(与文件系统的目录树类似),实际上正是这样的,内核变量ioprootdevicenode指示这颗树的根。!devnode命令可以看出这个根节点的情况,如果你使用!devnode 0 1命令的话,你将活生生的看到一个windbg的devmgmt.msc版。实际上系统的setupdi(setupapi.dll导出的用于设备安装的api)就是通过这个来dump出所有的设备的。devmgmt.msc间接的使用这些api(你可别像我一样讶异.msc文件只是一个由mmc.exe解析的xml文件)。同样osr的devicetree肯定也使用了这些api。

    到现在为止你可能更加困惹,啥是啥的pdo,系统如何知道哪个总线附接至哪儿,以形成设备层次。秘密在于注册表,设备安装时通过.inf文件等向注册表加入内容指示系统的加载顺序。早先的.inf文件真的是好复杂,在我看来绝不亚于perl脚本。windows 2000为你自动做了太多太多了(我真想知道到底做了什么,嘻嘻)。

    注册表中hklm\system\currentcontrolset\enum与hklm\system\currentcontrolset\control\class共同协作来完成这样的任务,当然与legacy驱动程序一样离不开hklm\system\currentcontrolset\services了。为了完整的传述windows 2000/xp分层驱动模型,有必要在最后提及一下filter驱动程序,这是附加在总线驱动程序或是其他驱动上或下的一类驱动,用于增强,改变原有设备的某些功能。由刚提及的注册表中的enum与class项的upperfilters与lowerfilters的值提供。

    最后,说明一下,由windows 2000/xp的这个分层驱动区分出很多概念,如中间层驱动程序,故名思义,如windows 2000/xp中随处可见的类驱动程序。类驱动程序实现某一类设备的共同功能,没有牵涉到实际硬件的访问。如磁盘类驱动程序,在windows 2000/xp中有disk.sys,tape.sys,cdrom.sys,他们均借助于classpnp.sys实现类驱动程序,以disk.sys为例,她根本不管是ide接口或是scsi接口,由底层的atapi.sys或是scsiport.sys这些miniport/port驱动实现与特定硬件的交互。

    另外再提及一点,可以说filesystem filter是一个legacy的分层驱动(只使用device_object的attacheddevice成员。而对于网络驱动程序,如ndis intermediate drivers(含ndis filter intermediate drivers与ndis mux intermediate drivers)也可以看作是分层驱动的应用,只不过在windows 2000/xp中由ndis wrapper library(ndis.sys)隐藏了太多的信息(隐藏了使用irp的真正面目),也可以这么说ndis.sys使用其内部自身的结构定义,如ndis_m_driver_block、ndis_miniport_block、ndis_protocol_block、ndis_open_block这些定义之间的微妙关系,自身实现了一个层次化的结构

文章整理:站长天空 网址:http://www.z6688.com/
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

文章页数:[1] 


放大字体显示 缩小字体显示 打印文章 推荐给朋友
热门文章
·使用C#在进度条中显示复制文件的进度-.NET教程,C#语言
·JAVASCRIPT调用JAVA-JSP教程,Java技巧及代码
·Java开源项目Hibernate包作用详解-JSP教程,Java技巧及代码
·新型单相逆变电源的研制
·解读Windows 2000/XP分层驱动模型-.NET教程,Windows开发
·NET中打印包含有格式的 RichTextBox 的内容-.NET教程,Asp.Net开发
·java用于链接数据库的例子(*.properties)-JSP教程,资料/其它
·cable modem及其系统的配置和使用
·asp.net 1.1/ 2.0 中快速实现单点登陆-.NET教程,Asp.Net开发
·持续集成 Java手册-JSP教程,Java技巧及代码
最新文章
·像我一样的菜鸟站长常犯的几种错误_站长心得
·网友开博客真的能赚钱吗?_网赚技巧
·google广告代码可以放到几个网站吗?_网赚技巧
·论坛如何最佳化?adsense最佳化案例_网赚技巧
·姚劲波:站长要用开放平和的心态去坚持_站长访谈
·章征军和他的站长网_站长访谈
·建自己想建的站坚持下去 就是最好的seo_站长心得
·autocad 2008的service pack 1测试版发布_autocad教程
·网站的定位在于选题(后半部分参考市场报)_站长心得
·互联网赚钱的几种基本模式_站长心得
相关主题
西部数码虚拟主机

友情链接
CNNIC 西部数码
万网 自助建站
虚拟主机 asp空间
域名注册 域名
域名申请 主页空间
论坛空间 网站空间
国际域名 虚拟空间
空间租用 DDOS防火墙
成都主机托管 四川主机托管
主机租用 服务器租用
网站目录 自助建站
虚拟主机 网址大全
软件下载
自助链接
虚拟主机资讯 特价虚拟主机
版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!
关于我们:站长天空:专业提供最新的站长资讯、在线教程、虚拟主机权威评测、虚拟主机性能对比、网站制作教程,开发教程,站长工具。包括网页制作教程、冲浪宝典、编程参考、操作系统、软件教学、行业动态等。
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。
发表评论 打印  刷新     关闭