驱动开发7.读写数据
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Devicwenku.baidu.comIoControl
result = DeviceIoControl(Handle, IOCTL_GET_VERSION_BUFFERED , InputData, InputLength, OutputData, OutputLength, &Feedback, &Overlapped);
读写数据
sigang@mti.xidian.edu.cn
应用程序如何读写数据
应用程序发起一个读或写操作时,它会提 供用户模式虚拟地址和长度,即应用程序 向I/O管理器提供了一个数据缓冲区。 应用程序读写数据主要通过如下三个win 32API: ReadFile,WriteFile,DeviceIoControl
通常,CPU的内存地址空间和I/O地址空间 是分离的。 为了访问“内存映射”设备,CPU用load或 store操作直接对一个虚拟地址进行内存引 用,然后CPU利用一组页表把虚拟地址转换 成物理地址 。 为了访问“I/O映射”设备,CPU必须使用 特殊的机制,如使用x86处理器上的IN和 OUT指令。
BOOL WriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped );
驱动程序如何读写数据
Buffered
当I/O管理器创建IRP_MJ_READ或IRP_MJ_WRITE请 求时,它探测设备的缓冲标志以决定如何描述新 IRP中的数据缓冲区。 如果DO_BUFFERED_IO标志设置,I/O管理器将分配 与用户缓冲区大小相同的非分页内存。 可以把如下代码假定为I/O管理器在ReadFile, WriteFile 调用发生后,驱动程序分发例程获得 IRP之前的代码。(实际上IO管理器肯定不是这样 实现的)读写数据.doc
如何指定缓冲区的读写方式
和驱动程序数据读写相关的Win32 API分为 两类,在不同的地方设置他们的缓冲区读 写方式 ReadFile,WriteFile(在AddDevice例程中 指定) DeviceIoControl(对每一个Control code 分别指定)
AddDevice中指定
内核模式驱动程序几乎从不使用用户模式 虚拟地址访问内存,因为不能确定具体的 线程上下文。 Windows 2000为驱动程序访问用户模式数 据缓冲区提供了三种方法:buffered, direct,neither。
三种方法
buffered方式中,I/O管理器先创建一个与用户模
式数据缓冲区大小相等的系统缓冲区。驱动程序 将使用这个系统缓冲区工作。I/O管理器负责在系 统缓冲区和用户模式缓冲区之间复制数据。 在direct方式中,I/O管理器锁定了包含用户模式 缓冲区的物理内存页,并创建一个称为MDL(内存 描述符表)的辅助数据结构来描述锁定页。驱动程 序将使用MDL工作。 在neither方式中,I/O管理器仅简单地把用户模 式的虚拟地址传递给驱动程序。这通常很少用到。
MDL的数据结构
typedef struct _MDL { struct _MDL *Next; CSHORT Size; CSHORT MdlFlags; struct _EPROCESS *Process; PVOID MappedSystemVa; PVOID StartVa; ULONG ByteCount; ULONG ByteOffset; } MDL, *PMDL;
DeviceIoControl
BOOL DeviceIoControl( HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped );
某些CPU有分离的内存地址空间和I/O地址 空间。例如,Intel架构的CPU同时支持这 两种地址空间。其它CPU,例如Alpha,仅 有一个内存地址空间。 如果设备是I/O映射的,PnP管理器将赋予 端口资源。如果设备是内存映射的,它将 赋予内存资源。 没有IO地址空间怎么办?
解决
描述
StartVa成员给出了用户缓冲区的虚拟地址。 ByteOffset是缓冲区起始位置在一个页帧中的偏 移值,ByteCount是缓冲区的字节长度。 内存中紧跟在MDL的后面有一个Pages数组,它虽 然没有被正式地声明为MDL结构的一部分,但包含 用户模式虚拟地址映射为物理页帧的个数。 所有的成员都不能够直接访问,必须通过一些宏 或者函数访问。原因 宏或者函数说明:读写数据.doc
结论
系统缓冲区地址被放在IRP的 AssociatedIrp.SystemBuffer域中,而数 据的长度被放到stack->Parameters联合 中 ,驱动程序中使用的就是这两个域 实际过程比这个复杂。
Direct方式
当I/O管理器创建IRP_MJ_READ或IRP_MJ_WRITE请 求时,它探测设备的缓冲标志以决定如何描述新 IRP中的数据缓冲区。 如果DO_DIRECT_IO标志设置,I/O管理器将利用用 户的虚拟地址和数据创建一个IRP。 可以把如下代码假定为I/O管理器在ReadFile, WriteFile 调用发生后,驱动程序分发例程获得 IRP之前的代码。(实际上IO管理器肯定不是这样 实现的)读写数据.doc
#define IOCTL_GET_VERSION_BUFFERED \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_GET_VERSION_DIRECT \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) #define IOCTL_GET_VERSION_NEITHER \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS)
使用
使用HAL的关键原因是可以不必担心平台的 不同,也不用担心Windows 2000的多任务 和多处理器环境对设备访问的特殊要求。 因此,工作将十分简单:使用PORT调用访 问端口资源,使用REGISTER调用访问内存 资源。 然而,有些程序直接以普通的内存指针的 方式访问内存影射的存储空间
ReadFile
BOOL ReadFile( HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped );
WriteFile
PDEVICE_OBJECT fdo; IoCreateDevice(..., &fdo); fdo->Flags |= DO_BUFFERED_IO;
<or>
fdo->Flags |= DO_DIRECT_IO;
<or>
fdo->Flags |= 0; direct nor //buffered // i.e., neither
为了避免为兼容各种平台而使用大量条件编译代 码,Windows NT的设计者发明了硬件抽象层(HAL)。 HAL提供了用于访问端口和内存资源的函数,如表。 有24个函数用于设备访问,使得WDM驱动程序不直 接依靠HAL做任何事,可以把这24个函数看成是 HAL的全部公共接口。 可以从或向一个PORT/REGISTER资源READ/WRITE一 个或一组UCHAR/USHORT/ULONG值。
Neither方式
如果在设备对象中同时忽略了 DO_DIRECT_IO和DO_BUFFERED_IO标志设置, 将得到默认的neither方式。对于这种方式, I/O管理器将简单地把用户模式虚拟地址和 字节计数交给驱动程序。 IO管理器做的工作类似于如下代码:读写 数据.doc
端口与寄存器
特点
一旦指定,所有的其它过滤驱动程序都要 使用这个数据缓冲方式 这个数据缓冲方式,即针对ReadFile又针 对WriteFile,一旦指定某种方式,读写都 要使用者这种方式。 无论使用哪种方式,对用户的接口,也就 是ReadFile和WriteFile的使用都是一样的
DeviceIoControl如何指定
内存资源
内存映射设备使得软件可以使用load和store指令 访问设备的寄存器。 可以在转换后的描述符中得到一个 CmResourceTypeMemory类型的描述符。这个描述 符里面的地址是一个物理地址,需要保留该物理 地址对应的虚拟地址,因为程序中要使用这个虚 拟地址。 后面,对这些寄存器的访问将使用处理内存寄存 器的HAL例程,如READ_REGISTER_UCHAR和 WRITE_REGISTER_UCHAR,等等。
端口资源
I/O映射设备把硬件寄存器暴露给某些种类的 CPU(包括Intel x86),软件可以使用I/O地址空间 寻址这些硬件寄存器。而其它种类的CPU没有分离 的I/O地址空间,因此它们使用常规的内存引用来 寻址这些寄存器。 幸运的是,WDM驱动程序中完全可以不用了解这些 复杂的寻址过程。如果设备使用一个端口资源, 那么可以直接在转换后的描述符中得到一个 CmResourceTypePort类型的描述符。 通常需要在设备扩展中保存这个描述符三个方面 的信息。例子
特点
对不同的CTL_CODE可以有不同的缓冲方式 无论使用哪种方式,用户接口,也就是 DeviceIoControl的使用都是一样的 DeviceIoControl中可以既指定输入缓冲区 也指定输出缓冲区
驱动程序中访问用户缓冲区
对于buffered,direct,neither方式各有 不同的访问方式 前提: 要熟悉IRP的数据结构 要熟悉IO管理器在用户和驱动程序之间交 互时做了什么 下面看在这三种方式中IO管理器做了什么 (只针对ReadFile和WriteFile)
体系结构
南北桥体系结构。(也有芯片组把这两者做 到一块的) 设备使用总线专有的方式解码内存和I/O地 址。在PCI总线中,主PCI桥(北桥)把CPU 物理地址和I/O地址映射到总线地址空间, 而设备可以直接访问总线地址空间。 对于两种地址空间都支持的CPU,设备配置 空间中的标志位决定了主PCI桥是把设备寄 存器映射成内存地址还是映射成I/O地址。
解决
这些访问函数的内部实现与平台高度相关。 例如,Intel x86版本的READ_PORT_CHAR函数用IN 指令从指定的I/O端口读一个字节。而在Windows 98中,直接把驱动程序对这个函数的CALL指令直 接替换成IN指令。该函数的Alpha版本则执行一个 内存提取。 READ_REGISTER_UCHAR函数的Intel x86版本将执 行一个内存提取;而该函数的Alpha版本则是一个 直接内存引用的宏。该函数的缓冲区版本, READ_REGISTER_BUFFER_UCHAR,在x86环境中需要 做一些额外工作,以确保在操作完成时所有CPU的 高速缓存都被正确刷新。
结论
I/O管理器首先创建一个描述用户缓冲区的MDL。 IoAllocateMdl的第三个参数(FALSE)指出这是一 个主数据缓冲区。最后一个参数(Irp)指定该MDL 应附着的IRP。 在内部,IoAllocateMdl把Irp->MdlAddress设置 为新创建MDL的地址,以后驱动程序将使用这个成 员,并且I/O管理器最后也使用该成员来清除MDL。 通常不能对MDL直接访问。内核提供了一套函数操 作MDL