Windows文件系统的过滤器驱动程序设计
西安电子科技大学 李新
摘要:某些应用程序对文件系统的性能有较高要求。例如媒体播放器需要满足最小数据传输率才能保证视觉上的流畅。由于Windows文件系统本身没有提供这样的保证,需要编写过滤器驱动程序添加这项功能。本文首先介绍系统驱动体系和文件系统工作机制,然后分析文件系统过滤器驱动程序的功能特点,最后介绍一种满足此类应用程序传输带宽的总体解决方案(来源于文献1)。
关键词:文件系统 过滤器驱动程序 设备对象堆栈
一.基础知识
1. 系统组件
Windows NT操作系统含有许多功能相互独立的内核模式组件。如内核I/O管理器、硬件抽象层、存储管理器、配置管理器、对象管理器、运行支持和过程结构等组件。Windows 2000在此基础上增加了即插即用管理器和电源管理器。两种系统分别采用不同的驱动模型。本文整体上以Windows 2000的文档为主。不过文件系统具有特殊性(非WDM),在两个系统中的运行机制基本相似。在这些内核组件中,I/O管理器最为关键,它由文件系统、中间层驱动程序和最低层设备驱动程序三部分组成,对所有的核心态驱动程序提供统一的通信接口IRP(I/O请求包方式)。应用程序的I/O操作都是通过这种方式调用I/O管理器的服务完成的。主要服务有:配置管理、内存管理、对象管理、安全监视等等。
2. 驱动程序
驱动程序实质是能被操作系统加载调用,为系统设备实现相应功能的内核模式的动态链接库。形式上可以看作是一个包含许多例程的容器。当第一次安装时,由I/O管理器调用驱动程序入口函数DriverEntry,驱动程序在此进行自身初始化,设置其它例程的进入点,使操作系统接下来可以调用这些服务例程。驱动程序加载时机与它的启动类型和启动组设置有关。启动类型有五种,通常文件系统及其过滤器驱动程序属于SERVICE_BOOT_START或SERVICE_DEMAND_START。
驱动体系是分层的。在用户程序和硬件设备之间可以存在多个驱动程序,这些驱动程序上下链接形成驱动程序堆栈(实际数据结构是由这些驱动程序创建的设备对象所构成的设备堆栈),共同为此硬件设备服务。I/O管理器根据请求向设备驱动程序创建并发送的IRP,会沿设备对象栈依次下传,直到某个驱动程序完成此IRP请求的操作。物理设备堆栈在物理设备枚举过程中形成。当系统启动后,PNP管理器从系统根总线开始检测PNP物理设备,为之创建物理设备对象(PDO),然后根据注册表加载它的驱动程序,创建其功能设备对象(FDO);然后这些设备的驱动程序再检测连接在它上面的PnP硬件,同样为各硬件创建PDO,加载其驱动程序,创建FDO。重复直到枚举完毕。每个物理设备的PDO和FDO(如果该设备具有过滤程序,还将有FiDO)形成一个堆栈结构,称设备堆栈。枚举得到的设备构成物理设备树。节点主要就是设备堆栈。文件系统不是物理设备,其堆栈结构比较特殊,也不作为物理设备树的节点,但构成的原理及其运行机制是相似的。
过滤器驱动程序是一种可选择的特殊驱动程序,可以加载在其它驱动程序之上,用于修改或增加原驱动程序的功能,而不必修改原驱动程序和使用该驱动的应用程序。例如,只需在过滤驱动程序中添加处理例程,把数据写到两个不同的物理磁盘,保证数据的冗余保存,增加磁盘访问的容错能力。使用IoAttachDeviceByPointer(或IoAttachDeviceToDeviceStack.)把一个过滤设备对象FiDO链接到目标设备对象FDO上。步骤:⑴获取目标设备对象的指针;⑵创建过滤设备对象;⑶保证此过滤驱动程序能够处理原目标设备接收的所有IRP。即为所有能接收的IRP设置派发例程入口点,并保证透明性;⑷调用IoAttachDeviceByPointer创建链接,把FiDO填入设备堆栈,使之紧紧位于目标设备对象FDO之上;当上述操作完成后,I/O管理器就把所有发往目标对象的IRP重定向发给该过滤设备对象。这样过滤程序就可以在目标设备驱动程序之前对IRP进行检查、修改、完成等操作。驱动程序通过I/O stack location中的主功能码Major Function Code和次功能码获取任务信息。除标准功能码外,还可进行自定义,然后在用户程序中使用DeviceIoControl函数通知I/O管理器创建具有自定义功能码的IRP,由此可以完成一些特殊操作或实现驱动程序与应用程序之间的通信。举例说明IRP的派发过程及内核I/O管理器的功能:当应用程序请求打开某个文件,会有以下过程:
1) 保护子系统调用I/O管理器提供的服务来打开一个命名文件;
2) I/O管理器调用对象管理服务查出所要操作的文件的符号连接名(SymbolicLink),然后调用安全监视服务判断该用户保护子系统是否有访问此文件的权限;
3) I/O管理器对文件进行定位。如果成功,继续处理这个请求;
4) I/O管理器为这个打开请求创建I/O请求包(IRP);
5) I/O管理器把IRP传递给文件系统驱动程序。文件系统驱动程序通过收到的IRP中任务信息,确定应完成什么操作。首先检查参数确定数据是否已被缓存。如果没有为下一层的驱动程序设置IRP信息(对于每层驱动程序,IRP中都有一个数据域相对应-I/O stack location,用来保存相关的任务参数);
6) 驱动程序根据IRP执行I/O操作,将执行结果填入IRP;
7) 驱动程序将IRP返回给I/O管理器;I/O管理器从IRP得知I/O状态,然后通过保护子系统返回状态信息给原始调用者。
8) I/O管理器释放完成的IRP。
如果打开操作成功,I/O管理器向子系统返回文件对象的句柄。否则返回错误信息。
二.文件系统及其过滤程序
文件系统与内存管理器和缓存管理器子系统一起向用户提供访问非易失性存储器的能力。位于整个驱动体系的最上层。系统启动时文件系统的加载过程是:操作系统首先加载文件系统驱动程序及其过滤器驱动程序(如果有);然后I/O管理器创建全局文件系统队列,并对所有的文件系统及其过滤器程序进行初始化。第二步的具体情况是:⑴创建的全局文件系统队列分成四段,分别对应CD-ROM, Disk, Tape, Network类型;⑵除调用DriverEntry设置派发例程和创建设备对象外,还通过IORegisterFileSystem函数对文件系统程序进行注册;当注册完毕,其设备对象就按类型被添加到文件系统队列中的相应段里;⑶当所有类型的驱动程序初始化完毕后,I/O管理器执行驱动程序的ReInitialization例程(如果有)。
1.文件系统特点
文件系统驱动程序与其它设备驱动程序相比,有以下特点:
⑴ 文件系统驱动程序被保证调用环境是正在请求服务的线程环境。
当文件系统驱动程序处在驱动体系的最顶层,它的调用环境是发送此请求的用户线程环境。这保证文件系统能够使用Neither I/O内存地址寻址方式。然而过滤驱动程序可以位于文件系统驱动之上,因此编写文件系统过滤程序时需要考虑:不能改变调用环境,否则会导致文件系统驱动程序寻址失败,产生非法页面错误。为了避免这种灾难,可以改变数据缓冲区寻址方式,例如把Neither I/O改为Direct I/O。通过MDL(内存描述表)存放该用户程序的数据缓冲区的锁定的物理内存页地址信息。
⑵ 只有文件系统驱动程序为文件读写操作实现FAST I/O操作。
为了追求更好的性能,文件系统增加了FAST I/O例程,实现高速访问缓存数据的功能。不过只有在所需文件数据被缓存的情况下FAST I/O才会有效。当用户想访问某个文件时,I/O管理器首先调用FAST I/O例程。FAST I/O例程会返回一个布尔变量,告知是否能够进行FAST I/O处理。如果返回False,I/O管理器就不得不重新借助标准IRP的方式完成任务。
⑶ 文件系统堆栈与物理设备堆栈不同
文件系统堆栈有两类,分别由控制设备对象CDO或卷设备对象VDO与其过滤器设备对象构成。与设备驱动程序不同的,作为堆栈最低层的CDO或VDO是都是由该驱动程序自己创建的。CDO代表一个纯粹的文件系统程序,在注册之后就存入全局文件系统队列;VDO代表一个被CDO安装的卷。纯粹的文件系统程序与物理存储设备没有联系,访问物理设备要先创建与此物理设备连接的卷设备对象。卷安装过程大致如下:先通过I/O管理器创建卷参数表(VPB),存放目标物理设备堆栈顶层设备对象的地址信息;然后CDO文件系统创建VDO,形成新的文件系统堆栈(VDO堆栈),并通过设置VDO的VCB数据结构使其与VPB连接。图1显示一个安装了两个卷的文件系统,可以访问两个CDROM。
图1
⑷ 另外,文件系统驱动程序与内存管理器和缓存管理器子系统联系非常紧密。
2.文件系统过滤器驱动程序
程序基本框架:
w 过滤器驱动程序初始化:与设备过滤器程序类似,在DriverEntry进行初始化。 创建CDO,并创建此CDO的符号连接名;设置派发例程,包括FAST IO派发例程;做其它初始化;
w 连接到一个文件(或卷)系统:在派发例程中进行以下过程。 创建过滤器设备对象;连接到目标设备对象;设置与目标设备对象一致的寻找方式;清除初始化标志(DO_DEVICE_INITIALIZING),表示完成了连接过程的初始化。
w 当IRP到来,截获进行过滤处理。
安装方式:不能使用INF方式,只能通过Service Control Manager,在应用程序中执行以下步骤:
⑴ 获取Service Control Manager句柄;
⑵ 设置该程序所属的加载组的名称;
⑶ 获取系统路径,然后拷贝该程序到系统路径中;
⑷ 使用CreateService函数,以服务方式安装该程序;
⑸ 如果加载类型是SERVICE_DEMAND_START,使用StartService函数,加载该程序;
⑹ 关闭该程序的服务句柄和Service Control Manager句柄。
三.文件过滤器程序示例-为文件系统实现带宽保证功能的解决方案
许多应用程序的性能依赖磁盘(或网络)数据的传输能力。然而Windows文件系统本身没有提供这方面的功能(带宽速度和稳定性),需要编写文件系统过滤器驱动程序提供这项功能。基本思路是:由此类应用程序加载过滤器驱动程序,通过分析截获的IRP,在转发给原文件系统驱动程序之前,根据其带宽需求进行重新排序,达到对带宽需求大的应用程序优先服务的目的。
主要的问题是如何设计一种实时机制计算每个应用程序的带宽。磁盘带宽定义为单位时间内访问的字节总数。可以通过对磁盘的Read IRP进行计时,统计带宽。具体方法:在将IRP下传给文件系统程序之前,对其设置一个完成例程。当IRP被完成时IO管理器通知过滤程序调用完成例程。由此测定此IRP服务所用时间以及带宽。经过对多次IRP的统计可得带宽的平均值。由于带宽的不稳定性,只有带宽平均值才能作为判断标准。为了提高计算精度,进一步分析影响服务质量的主要因素,以便注意解决和避免。
· I/O请求的服务时间与不同文件系统的运行机制相关;
· 磁盘存储碎片会降低磁盘的传输速率;
· 当采用FAST I/O处理方式时,I/O管理器会把I/O请求参数直接传给文件系统驱动程序,能大大减少服务时间。但由于无法对服务时间进行统计,反而不能提供带宽保证;
· 由于系统进行磁盘特殊功能时会发出系统IRP,通常是请求进行页面操作,会占有大量带宽资源。当系统IRP突发时,可能难以保证用户程序的带宽需要。
下面介绍的解决方案来自参考文献1。该过滤器驱动程序框架如图2所示,由4个功能模块组成。
1. Reservation Manager:资源预定管理器
负责维护系统中带宽资源信息数据表,其中记录系统资源被应用程序占用或预定的详细信息。当收到应用程序的资源请求时,资源预定管理器为此应用程序进行数据库维护操作,如添加、修改、删除表项。
2. Collector:信息收集器
对截获的IRP进行解析,查出进行资源申请的用户程序名称。然后将此信息发给资源预定管理器。并且在把IRP转发给调度管理器之前,给IRP打上时间戳,记录此次转发时间。
3. Scheduler:调度管理器
对接收到的IRP进行调度。实现方式通过维护一个记录尚未发送的IRP的数据列表(PIL)。它先把从信息收集器接收到的IRP添加到PIL中,然后根据时间戳确定何时转发。当转发给派发器后,从表中删除此项数据。
4. Dispatcher:IRP派发器
负责转发IRP,并统计带宽。
图2
参考文献
:
1.
J. JosephPrabhu ,D. Narayanan, I/O File System Filter Driver For Windows NT
2.
Mircosoft Corp,Installable File systm Kit Document
3. Water Oney,
Programming Windows Driver Model