wdf模型开发虚拟磁盘

来源:互联网 发布:外贸海关数据查询 编辑:程序博客网 时间:2024/05/02 11:15

主要搞清楚物理磁盘的结构

整个物理磁盘的第一个扇区为MBR,BIOS加载完后将调用MBR。MBR中主引导程序负责从活动分区中装载和运行系统引导程序。MBR的引导程序后面是硬盘分区表(DPT),用占64字节,记录物理磁盘的每个逻辑分区的起始位置,从这个起始位置开始的部分成为DBR(系统引导记录)。DPT的后面是有效结束标志(0x55AA),没有这个标志,操作系统会认为磁盘没有初始化,无法正确加载磁盘的分区。
DBR包含DOS引导程序和BPB(数据描述区)。DOS引导程序完成DOS系统文件的定位与装载,BPB描述DOS分区的磁盘信息。
在DBR之后是Fat表,一式两份。Fat表的第0、1项被保留,从第2项开始记录文件所在的位置。FAT表实际是一个链表,它的每个表项的编号都代表磁盘上的一个簇,每一个表项的内容都是一个簇的编号,而一条这样完整的链表就代表了一个文件在磁盘上所占的所有的簇。用户文件是以簇为单位存放在磁盘数据区中,一个文件至少占用一个或多个簇
在Fat表之后紧跟着的是根目录入口点,fat表的每个簇链的起始位置由根目录入口点记录着,多个根目录入口点形成一种表。记录了文件属性以及在fat表中的起始位置。因而通过fat表可以获取整个文件或目录的所有簇。

在根目录入口点之后就是数据区。



入口函数

在DriverEntry函数内声明并初始化WDF_DRIVER_CONFIG类型的变量config。这个结构如下:
typedef struct _WDF_DRIVER_CONFIG {    ULONG Size;    PFN_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd; //初始化AddDevice函数例程,类似于WDM的AddDevice    PFN_WDF_DRIVER_UNLOAD    EvtDriverUnload;//初始化DriverUnload例程    ULONG DriverInitFlags;//设置驱动的初始化的类型,可以设置开发WDM模型的驱动    ULONG DriverPoolTag;//设置分配内存的标志} WDF_DRIVER_CONFIG, *PWDF_DRIVER_CONFIG;

使用WdfDriverCreate函数创建一个驱动对象。在参数内设置驱动对象的属性和配置信息。属性结构体如下:


EvtDriverDeviceAdd函数

在DriverEntry函数执行完后,这个驱动只能依靠EvtDriverDeviceAdd函数和系统保持联系,系统在运行过程中一旦发现了这种类型的设备,便会调用此函数。
在函数的参数中有一个PWDFDEVICE_INIT类型的DeviceInit参数。这个参数是一个不透明的结构体,专门传递给此函数用来设置设备属性和创建设备。
(a)设置设备属性并创建设备

 status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);//为这个设备指定设备名称  WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_DISK);//指定设备类型  WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);//指定设备读写方式  WdfDeviceInitSetExclusive(DeviceInit, FALSE);//设置设备的独占方式(设备是否可多次打开)

(b)设置设备扩展
使用此宏设置WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_EXTENSION);设备扩展,创建设备后获得设备扩展指针。
(c)设置设备清除回调例程
通过设备属性参数设置deviceAttributes.EvtCleanupCallback = RamDiskEvtDeviceContextCleanup;
(d)创建设备
通过WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);创建设备,参数包括设备init结构和设备属性。

有关发送到这个设备的请求处理,在此使用wdf框架中的队列对象,首先初始化队列对象,然后对特殊处理的消息例程进行注册回调函数,为设备建立这个队列以后,WDF驱动框架会自动将所有发往这个指定设备的请求都放入这个队列中处理。
(a)初始化队列默认例程
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE ( &ioQueueConfig, WdfIoQueueDispatchSequential );//串行化处理请求
(b)将特殊处理的例程进行注册
ioQueueConfig.EvtIoDeviceControl = RamDiskEvtIoDeviceControl;//
   ioQueueConfig.EvtIoRead          = RamDiskEvtIoRead;//
   ioQueueConfig.EvtIoWrite         = RamDiskEvtIoWrite;//
(c)设置队列扩展
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes, QUEUE_EXTENSION);
(d)创建队列 status = WdfIoQueueCreate( device, &ioQueueConfig, &queueAttributes, &queue );//在配置中设置队列特征,在属性中设置队列扩展
(e)接下来需要初始化与内存盘相关的一些数据。这这里是有的设备扩展来来存放这些数据结构的内容。同时在注册表中开辟了一个键用来存放可配置的参数。这些参数对应驱动中就成了一个DISK_INFO结构,是有RamDiskQueryDiskRegParameters函数读取注册表中的配置信息将设备扩展中的DISK_INFO结构进行填充。
(f)使用ExAllocatePoolWithTag函数开辟内存空间作为磁盘的容量,内存空间保存在设备扩展结构中。
(g)在开辟内存成功以后,需将调用自定义函数RamDiskFormatDisk将磁盘进行格式化操作。然后调用WdfDeviceCreateSymbolicLink创建设备扩展,为应用层提供访问名称。

Fat12/16初始化


磁盘结构是由制造过程中的物理结构决定的,而操作系统对磁盘的管理是通过文件系统来实现的,是一种逻辑上的结构。以下说明fat12/16的结构:


MBR:主引导记录的简称,位于整个磁盘的第一个扇区,大小正好是一个扇区的大小。MBR起始处是一段程序(占用446字节),在程序的后面是一个硬盘分区表(DPT,占用64字节),用于记录当前磁盘的具体分区信息。在DPT共64个字节中,以16个字节为分区表项单位描述一个分区的属性。也就是说,第一个分区表项描述一个分区的属性,一般为基本分区。第二个分区表项描述除基本分区外的其余空间,一般而言,就是我们所说的扩展分区。
最后的两个字节“55 AA”(偏移1FEH~偏移1FFH)是分区有效结束标志


DBR:操作系统引导记录的简称,在DPT中记录每个逻辑扇区的起始位置,DBR就存在于这个起始位置指向的第一个扇区里。DBR里面包括了有效的引导程序、厂商标志、描述数据区等。引导程序是一段用来加载真正操作系统的程序,在DBR的最开始是一个跳转指令,跳到DBR后面一点的程序处。厂商标志又叫OEM串,一般由格式化程序填写。数据描述区又叫BPB数据块,记录的信息用于系统在为这个逻辑盘建立文件系统是做初始参数,例如文件系统格式、根目录大小和簇大小。
作为文件系统的一个组成部分,BDR是操作系统的格式化程序建立的,在文件系统操作任何一个磁盘卷时,这一部分信息将被读取并作为文件系统在这个磁盘卷上的参数使用。


FAT区:文件分配表的简称。位于DBR之后并且以一式两份的形式连续保存。FAT表实际是一个链表,它的每个表项的编号都代表磁盘上的一个簇,每一个表项的内容都是一个簇的编号,而一条这样完整的链表就代表了一个文件在磁盘上所占的所有的簇。FAT表的第0项和第1项被保留,从第2项开始记录某个文件所在的位置。


根目录入口点:紧跟着FAT表存储,每个簇链的起始位置由根目录入口点记录着,多个根目录入口点形成一种表,这个表的每个表项代表了根目录下的一个文件或一个目录,这个表项里面记载了很多信息,例如:文件或目录的名字、属性修改日期等,最重要的是记录了这个文件或目录在FAT表中的起点,这个就可以通过FAT表找到这个文件或目录的所有簇,进而获取所有数据。


调用RamDiskFormatDisk函数开始进行磁盘格式化操作
(a)首先初始化设备扩展的DISK_GEOMETRY结构,这个结构是wdk定义的,用于设置磁盘物理结构的信息。结构体如下:
typedef struct _DISK_GEOMETRY {
   LARGE_INTEGER Cylinders;//一共有多少个柱面
   MEDIA_TYPE MediaType;//磁盘介质的类型
   ULONG TracksPerCylinder;//每个柱面有多少磁道
   ULONG SectorsPerTrack;//每个磁道有多少扇区
   ULONG BytesPerSector;//每个扇区的大小
} DISK_GEOMETRY, *PDISK_GEOMETRY;
(b)初始化文件系统和磁盘相关的参数-----根目录入口点数,这个参数决定了根目录中能够存在多少个文件和子目录。同时初始化的还有每个簇有多少个扇区组成,这是根据用户指定的数据来初始化的。
根据注册表中获得的录入口点数,并根据实际调整大小,以便最大限度的充分利用(若不是256的倍数,则设置比实际值稍大的256的倍数)
(c)使用BOOT_SECTOR的类型的bootSector变量指向了磁盘映像文件的首地址,磁盘映像文件最开始应该存储的是DBR。即bootSector指向的是磁盘卷的DBR。
(d)紧接着DBR之后是FAT表的初始化。在第一个表项内填写介质标识
(e)紧接着fat表之后便是根目录入口点,在Fat12/16文件系统中,通常第一个根目录入口点存储了一个最终被作为卷标的目录入口点,这里初始化它,之后这个磁盘卷算是初始化完毕了,也就可以使用了。

驱动中的请求处理

读写请求

在获取所有必需的参数之后,作为以磁盘内存为介质的模拟磁盘介质来说,只需要将内存中适当地点、适当长度的数据拷贝到读缓冲区,或将写缓冲区中的数据拷贝到内存镜像中即可。
在真实应用中,在磁盘之上的文件系统设备会根据Fat表等数据结构,将对文件的访问转化为对磁盘设备的访问,而磁盘对于上层来说,就是一个起始位置为0、总长度为磁盘卷总大小的扁平的寻址空间,任何由文件系统转换过来的访问都这个空间之内。
读写例程操作如下:
WDF_REQUEST_PARAMETERS_INIT(&Parameters);//初始化请求对象参数结构
   WdfRequestGetParameters(Request, &Parameters);//从请求中获取参数信息
WdfRequestRetrieveInputMemory或WdfRequestRetrieveOutputMemory//写入内存对象或从内存对象读出
WdfMemoryCopyToBuffer//将内存对象的内存拷贝到buffer内
WdfRequestCompleteWithInformation//完成请求

DeviceIoControl请求

获取DBR信息或者GEOMETRY信息等等,通过设备扩展内记录的信息进行内存填写,然后使用WdfRequestCompleteWithInformation函数完成请求。


编译和安装

通过inf文件和控制面板的硬件安装向导完成。

0 0
原创粉丝点击