首页
社区
课程
招聘
[原创]Windows 存储驱动开发学习
发表于: 2014-6-18 14:17 53933

[原创]Windows 存储驱动开发学习

2014-6-18 14:17
53933

由于Windows磁盘过滤驱动是一个大块的技术, 而DiskPerf只是其中的一个框架例子。所以,我们有必要学习一下 Storage Driver的基本知识。
我根据自己的技术积累,写一写对Storage Driver的理解。
我写的不好,大家见笑了。

Storage Driver Architecture(存储驱动体系结构)
来自APP或者内核组件的I/O请求经过I/O系统服务以及像文件系统驱动这样的上层驱动到达Storage class driver,Storage class driver将其翻译为系统定义的SCSI请求块(SRBs),其中SRB包含SCSI命令描述符块(CDBs),然后再将IRP发送到下层驱动(storage port driver或者intervening filter driver),好像设备就是一个SCSI设备一样(实质可能不是SCSI设备)。Storage port driver将来自Storage class driver的SRBs翻译为总线指定的命令,然后将所得命令发送到storage HBA,这期间需经过I/O总线驱动(可能)以及一个或多个过滤驱动。



Storage port driver为Windows NT的所有storage class drivers提供了一个接口。这些存储类驱动包括disk、tape、CDROM、DVD和changer类驱动。存储端口驱动将存储类驱动与各自所连接的HBA的适配器指定的请求进行了隔离。操作系统为SCSI、IDE、USB和IEEE 1394适配器都提供了存储端口驱动。
为SCSI总线服务的storage port driver将带有CDBs的SRBs传递到HBA指定的独立于操作系统的SCSI miniport driver。SCSI miniport driver是动态连接到SCSI port driver上的,以为SCSI HBA提供硬件特定的一些支持。
为IDE/ATAPI 或者IEEE 1394总线服务的storage port driver将接收到的来自storage class driver的SRBs翻译成下层适配器所要求的格式,比如,根据这些总线特定的传输协议重新打包CDBs,或者将它们翻译成不同的格式,以便将上层驱动从下层总线的特性中隔离开来。
上层或下层storage filter driver支持系统提供的storage class driver所不提供的设备特定的功能。下层过滤存储驱动监控SRBs和/或storage class driver发送的IRPs,并根据需要做相应的修改,然后才发送到下层驱动(storage port driver或者其它storage filter driver)。
Storage class driver利用SCSI port/class接口去控制存储类设备,系统为这些设备所连接的总线提供了storage port driver。
设备所连接的总线的类型以及该总线的storage port driver的实现对上层驱动是透明的。Storage port driver需要根据port/miniport driver体系结构,比如SCSI port driver;或者根据控制一个单一的、标准的硬件片,比如IDE/ATAPI port driver的一个庞大的驱动;或者根据某个过滤驱动,将接收到的SRBs翻译成不同的驱动栈所要求的格式,比如IEEE 1394 port driver。

Storage Drivers and Device Objects(存储驱动和设备对象)
存储设备堆栈由一棵设备对象树组成。这棵设备对象树是由处理发送给存储设备的I/O的驱动创建的。该设备对象树的根是一个FDO,该FDO是为存储适配器或者其它集成到该存储设备堆栈的驱动堆栈而创建的。该设备对象树上的设备对象都是供文件系统和APP使用的。
和其它所有的PnP driver一样,storage class driver或者storage filter driver将其自身添加到该设备对象树中。这个过程是在AddDevice例程中实现的:先调用IoCreateDevice创建设备,再调用IoAttachDeviceToDeviceStack将所创建的设备添加到设备堆栈的顶层上。
Tape miniclass、Medium Changer miniclass以及SCSI miniport driver不需要将创建的设备对象挂到设备堆栈上,而是由系统提供的tape class、changer class或SCSI port driver来处理这些任务。
Storage port drivers创建的PDOs类型是FILE_DEVICE_MASS_STORAGE。Disk class、CD-ROM class、Tape Class和Changer class drivers创建的FDOs的类型分别是:FILE_DEVICE_DISK、FILE_DEVICE_CD_ROM、 FILE_DEVICE_TAPE和 FILE_DEVICE_CHANGER。

SCSI HBA设备对象实例


(1)Storage bus driver为storage bus创建FDO(PCI bus FDO),并挂到由PnP管理器创建的storage bus PDO上。在storage bus FDO以下的设备对象树的结构取决于storage bus以及其如何集成到系统中的。在端口驱动层之上的Storage drivers不能与storage bus driver下层的对象进行交互。
(2)PCI bus driver枚举连接到该总线上的所有storage adapter,并为它们创建PDO。与各自adapter相对应的storage port driver创建一个FDO并挂到相应的PDO上。

上图中介绍了2个连接到PCI总线上的适配器的PDOs:一个代表IEEE 1394 Controller(请参考IEEE 1394 Controller设备对象实例);另一个代表SCSI HBA(Host Bus Adapter)。SCSI port driver和相关的miniport driver就会创建一个FDO并挂到SCSI HBA的PDO上。
  (3)Storage port driver为挂到起适配器上的目标设备创建一个虚拟的PDO,即虚拟化目标设备(比如SCSI Adapter FDO)。上图中,介绍了SCSI port/miniport drivers创建的2个PDOs:一个代表HDD,另一个代表CD-ROM。
  (4)一个或多个过滤驱动可以将一个过滤设备对象(filter DO)挂到storage port driver导出的目标设备PDO上。这样的filter DO可以截取并修改class driver发送到目标设备的请求。上图中,介绍了CD audio filter driver将一个filter DO挂到CD-ROM的PDO上。
  (5)Storage class driver创建一个FDO,并将其挂到下层设备对象上。这里的下层设备对象可能是storage port driver导出的目标设备PDO,也可能是一个中间层过滤驱动挂到该设备堆栈上的一个filter DO。上图介绍了2个这样的FDOs:一个代表CD-ROM设备;另一个代表HDD的Partition 0。Partition 0代表整个原始磁盘,且不管磁盘是否分区,其都一直存在。
  (6)Class driver也可以像总线驱动那样工作,当PnP管理器查询其子设备(IRP_MN_QUERY_DEVICE_RELATIONS with BusRelations)时,该类驱动返回一个PDOs的列表。例如,像可移动硬盘这样的已被分区的媒体设备驱动就可能返回代表其分区的PDOs列表。高层驱动则可以将其FDOs挂到这些PDOs上。上图中介绍了3个这样的PDOs,每一个都代表一个磁盘分区,该磁盘分区可以被当做一个目标设备。在固定的硬盘中,分区管理器挂到代表Partition 0的FDO上,并处理所有分区上的PnP操作。分区管理器的活动对磁盘class driver和所有上层过滤驱动来讲是透明的。
  (7)在Class driver之上可以有一个或多个过滤驱动。与下层过滤驱动不同的是,上层过滤驱动截取发送到class driver的IRPs,并可修改它们;它们还可以截取所有的read-write请求并根据需要决定是否传输数据,并定义额外的I/O control codes(IOCTLs),例如,要求APP提供密码或其它相关信息。上图中介绍了一个磁盘加密过滤驱动创建的filter DO,该filter DO被挂到代表磁盘Partition 1的PDO上。

IEEE 1394 Controller设备对象实例


(1)SCSI Adapter PDO以上的storage bus FDO的设备树,请参考SCSI HBA设备对象实例。
(2)IEEE 1394驱动堆栈中最顶层的驱动创建了一个SBP2磁盘设备PDO。IEEE 1394驱动堆栈最终发送SBP2命令到IEEE 1394总线上的目标CD-ROM设备。
(3)系统提供的IEEE 1394 storage port driver被当成一个过滤驱动,其创建一个filter DO并挂到SBP2磁盘设备PDO上。IEEE 1394 storage port driver将来自CD-ROM class driver的SRBs翻译成SBP2命令,这些命令将被发送到下层的IEEE 1394驱动堆栈中。该驱动提供给下层storage drivers的接口与SCSI HBA设备对象实例中介绍的SCSI port/miniport driver提供的接口一样。
(4)CD –ROM class driver创建一个FDO并将其挂到下层设备对象上。这里的下层设备对象可能是一个SBP2 port filter DO,也可能是其它过滤驱动挂到该设备堆栈上的filter DO。



(1)PCI总线驱动为PCI总线创建一个FDO,并将其挂到PCI总线的由PnP管理器创建的PDO上。
(2)PCI总线驱动枚举adapters和controllers,包括所有的IDE controllers,并为它们各自创建一个PDO。
(3)IDE controller driver,与其IDE controller minidriver一起,创建一个FDO,并将其挂到代表该控制器的PDO上。
(4)IDE controller driver“枚举”该控制器的通道。在效果上,意味着其创建2个PDOs:每个PDO代表该控制器的一个通道(个人理解:IDE控制器只有2个通道),并将它们都挂到该控制器的FDO上。
(5)IDE channel driver创建一个FDO并将其挂到代表该通道的PDO上。
(6)IDE channel driver枚举连接到该通道上的设备并为它们各自创建一个PDO。
(7)Disk class driver创建一个FDO并将其挂到相应的disk PDO上;CD-ROM driver创建一个FDO并将其挂到相应的CD-ROM PDO上。过滤驱动DO可插在设备PDO和设备FDO之间。

USB Mass Storage Device设备对象实例

复合USB Mass Storage Device的设备对象树



(1)PCI bus driver枚举USB host controller。系统加载USB port driver(usbport.sys)和配套到miniports(上图为提示)。之后,usbport.sys为该USB host controller创建一个FDO。
(2)USB port driver从根hub开始枚举系统中的USB hubs。Usbhub.sys驱动管理所有的USB hubs。上图仅展示了一层hub设备对象,但USB是允许hub设备级联的,所以在该设备树上可能潜在地存在很多hub设备对象。Hub driver检测并枚举USB mass storage device并为它们各自创建一个PDO。
(3)Windows提供了一个USB storage port driver (usbstor.sys),该驱动在USB堆栈和原始的Windows storage class drivers之间提供一个接口服务。Usbstor.sys创建其自己的FDO。Usbstor.sys可以将物理存储设备分割成多达16个逻辑单元。以上图所描述的为例,USB存储设备为Compact Flash device和Smart Media device提供了相互独立的Slots。所以,在这个例子中,usbstor.sys创建了2个独立的PDOs:一个代表Compact Flash device;另一个代表Smart Media device。
(4)usbstor.sys之上的堆栈由原始的disk class driver按普通方式管理。Disk class driver为Partition 0(代表整个磁盘) 创建一个PDO和一个FDO,并为每个分区创建一个PDO。
(5)分区管理器为每个磁盘分区创建一个FDO。

为了预防死锁,storage driver中服务于read或write请求的所有代码都不能是分页代码,也不能试图访问分页内存。这是因为存储驱动的DispatchRead和DispatchWrite例程能在IRQL > PASSIVE_LEVEL上被调用,且缺页请求发生在IRQL == APC_LEVEL。
同样,存储驱动的DispatchDeviceControl例程一定不能包含分页代码,也不能访问分页内存。
然而,微软要求所有的storage IOCTL请求能在PASSIVE_LEVEL上提交,这样,即使该dispatch routine自己不是分页代码,但它能够调用分页的子例程以处理storage IOCTL请求。这些子例程也可以访问分页内存。
像DriverEntry、Reinitialize和Unload这样的例程,不处理I/O操作,且运行在IRQL == PASSIVE_LEVEL,所以它们可以有分页代码。
管理storage devices的驱动在页路径上需考虑其它方法。如果驱动参与了处理分页文件中的I/O操作,则其处于“paging path”中。当一个storage driver处于分页路径中时,其处理IRP_MJ_POWERD的DispatchPower例程就一定不能是分页代码。
默认情况下,kernel-mode drivers的代码都是非分页的,分页的kernel-mode drivers也不能使用global memory。

Storage devices在将数据写到永久性媒介之前,比如磁盘盘片,通常先将数据缓冲到write cache中。这类型的缓冲区虽然完善了设备性能,但可能降低数据的完整性。如果write cache没有后备电池,则电源错误发生后可能导致缓存的数据丢失。
补救数据丢失问题的方法是刷新write cache(利用SCSI设备上的SCSI SYNCHRONIZE CACHE命令)。然而,刷新write cache需付出昂贵的代价,并且,如果频繁刷新,也会大大降低性能。为了不使用刷新write cache这个操作,很多storage devices提供了write-through请求。该请求绕过write cache并直接将数据写到媒介中。例如,数据库应用程序可以使用write-through请求以确保事务日志能到达媒介,文件系统驱动可使用write-through请求以确保文件系统媒介数据到达媒介。
然而,并不是所有有write caches的storage devices都支持write-through请求或者SYNCHRONIZE CACHE;且一些设备也不需要采取绕过或刷新缓存数据的方式来预防数据丢失,因为它们的write caches有后备电池系统来预防电源错误发生时导致数据破坏的问题。应用程序和驱动在有效使用write cache之前必须充分了解该设备的write cache的属性。
在Windows Vista中,使用IOCTL_STORAGE_QUERY_PROPERTY,且属性标识为StorageDeviceWriteCacheProperty的请求来查询设备的write cache的属性。Write cache的属性包括如下信息:
   Presence of a write cache:指示该设备是否存在write cache。
   Type of write cache:Write cache主要有2大类型:write back和write through。有write-back cache的设备在绝对需要之前不能将缓存数据拷贝到永久性媒介上。这个操作优化了写操作的性能。有write-through cache的设备并行地将数据写到缓冲区和媒介上。这不能优化写操作,但其加快了后续的读操作。
  Write-through优化了读操作;
  Write-back优化了写操作。
不要混淆write-through cache和write-through request。如果设备支持write-through requests的话,Write-through request能被所有类型的cache使用,包括write-back cache。例如,假设目标设备是一个有write-back cache的SCSI 设备。如果设备支持write-through requests,则请求发起者可以通过设置写命令的CDB的force unit access (FUA)位绕过write cache。
  Sync cache support:该属性表明设备是否支持SCSI SYNC CACHE命令,或者其它总线上的与该命令功能相同的命令。
  Battery backup:该属性值表明设备是否有备用电池以保护电源错误发生期间的数据的完整性。

存储类驱动介绍
无论mass storage device挂在什么类型的总线上,storage class driver都会利用已经建立好的SCSI class/port接口来控制它们。当然,系统会为挂载了mass storage device的总线提供storage port driver(目前有:SCSI、IDE、USB和IEEE 1394)。Mass storage device所挂载的总线到底是什么类型,对storage class driver来将是透明的。
所有storage class driver都需要处理来自APP或上层驱动的I/O请求,然后穿过下层过滤驱动,最后到达storage port driver。Storage class driver的上层驱动主要是创建包含CDBs的SRBs,然后作为IRP的一个参数,并将该IRP发送给storage class driver。
Storage class driver在SRB中不提供寻址信息,而port driver(或一个still-lower driver)负责处理所有的寻址请求。Storage port driver将SRBs翻译成下面的HBA所要求的格式的命令并发送到设备。这里下面的HBA可能是SCSI HBA、1394 HBA、IDE Controller或其他类似的硬件。在WDK中,“HBA”代表下层类似的适配器或控制器。
Storage class driver必须实现DispatchSystemControl例程,以处理system-control IRPs。

(一般功能) Storage Class Driver's General Functionality
相对于storage port driver来讲,storage class driver是一个高层驱动,其实现了具体设备类型的功能。一般情况下,所有的storage class driver都需完成一下功能:
声称 PnP管理器传递到其AddDevice例程中的所有PDO(由PnP管理器创建)所代表的设备。
为每一个PDO创建一个FDO,并挂到其设备堆栈上。
如果该storage class driver控制的是一个可分区的设备,则其还需为每个分区创建一个PDO,并相应枚举请求。
解析系统I/O请求(IRPs)。
将IRPs映射为SCSI class/port接口请求(带有SCSI  CDBs的SRBs)。
为请求建立超时值(不同的设备类型,超时值可能不同)。
将数据传输大小限制到适合下层HBA的值。(将一个IRP分成多个IRP)
处理storage port driver没有处理的错误,比如check-condition状态或者总线重置。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 3
支持
分享
最新回复 (18)
雪    币: 557
活跃值: (449)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
2
就先总结这么多了,其他的,有空再写。
请各位多多包涵了。 若写的不对,不好,还请您指点。
2014-6-18 14:21
0
雪    币: 221
活跃值: (2326)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
USB Mass Storage Device设备 驱动有范例? 来学习学习一下。
2014-6-18 14:27
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hhs
4
过来膜拜的的。。
2014-6-19 10:39
0
雪    币: 69
活跃值: (242)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wmg
5
不错,学习了
2014-6-20 09:20
0
雪    币: 53
活跃值: (42)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
2014-6-20 14:48
0
雪    币: 557
活跃值: (449)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
7
我的东西,可能对有的人来说,太简单了。
改天,给大家一个硬件虚拟化的驱动程序代码。
2014-7-9 16:10
0
雪    币: 49
活跃值: (19)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
是的理论太多 不操练下 感觉很空
希望干货东西 早日面世
2014-7-9 20:11
0
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
很规范很详细。。。很通俗易懂。支持
2014-7-9 20:26
0
雪    币: 221
活跃值: (2326)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
10
太谢谢了
2014-7-9 20:28
0
雪    币: 557
活跃值: (449)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
11
谢谢大家的鼓励
2014-9-10 11:15
0
雪    币: 36
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
很佩服楼主一丝不苟的精神
2014-9-10 13:51
0
雪    币: 3761
活跃值: (510)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
mark 学习了
2014-9-15 07:32
0
雪    币: 23
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
膜拜楼主
2014-11-11 23:25
0
雪    币: 25
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
感谢分享!
2014-11-12 09:30
0
雪    币: 40
活跃值: (284)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
牛B,你们为何都乳此的牛B呢?
2015-1-6 16:45
0
雪    币: 40
活跃值: (284)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
这些东西看起来,都太棒了啊,亲。雷锋同志你好哇
2015-1-6 16:47
0
雪    币: 96
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
请问原文中的
管理storage devices的驱动在页路径上需考虑其它方法。如果驱动参与了处理分页文件中的I/O操作,则其处于“paging path”中。当一个storage driver处于分页路径中时,其处理IRP_MJ_POWERD的DispatchPower例程就一定不能是分页代码。
是如何理解的呢?感觉如果存在分页文件,为何是 电源的分发函数 不能分页呢?为啥不是读写 或者deviceiocontrol  要求不能分页呢 呢?
2015-7-24 13:54
0
雪    币: 6509
活跃值: (2382)
能力值: ( LV12,RANK:320 )
在线值:
发帖
回帖
粉丝
19
支持一个!
2019-4-11 13:41
0
游客
登录 | 注册 方可回帖
返回
//