📄 华程电子 - 分类应用(usb开发)软件篇.htm
字号:
写输出缓冲区或丢弃输入缓冲区 <BR>FlushFileBuffers
<BR>FlushConsoleInputBuffer <BR>PurgeComm <BR>IRP_MJ_SHUTDOWN
系统关闭InitialSystemShutdown
<BR><BR>和上面的驱动程序支持的功能代码相对应,一般的驱动程序看起来就象下面的样子。 <BR>DriverEntry(…) // 驱动程序入口 <BR>{
<BR>… <BR>DeviceObject->MajorFunction[IRP_MJ_CREATE] = XXDriverCreateClose;
//XX对应的是你自己给你的驱动程序的命名<BR>DeviceObject->MajorFunction[IRP_MJ_CLOSE] =
XXDriverCreateClose; <BR>DeviceObject->MajorFunction[IRP_MJ_READ] =
XXDriverReadWrite; <BR>DeviceObject->MajorFunction[IRP_MJ_WRITE] =
XXDriverReadWrite; <BR>… <BR>} <BR>XXDriverCreateClose(…) //
对应IRP_MJ_CREATE和IRP_MJ_CLOSE的例程 <BR>{ <BR>//………. <BR>}
<BR>XXDriverDeviceControl(…)// 对应IRP_MJ_DEVICE_CONTROL的例程 <BR>{ <BR>//………. <BR>}
<BR>XXDriverReadWrite(…) // 对应IRP_MJ_READ和IRP_MJ_WRITE的例程 <BR>{ <BR>//………. <BR>}
<BR><BR>一个驱动程序并不需要支持所有的功能代码,比如如果一个驱动程序根本就不必要与用户模式客户程序交互,那么就不用支持IRP_MJ_CREATE和IRP_MJ_CLOSE。又如设备不支持设备读写,就不用支持IRP_MJ_READ和IRP_MJ_WRITE。
驱动程序对象是在操作系统启动驱动程序、在调用驱动程序 入口DriverEntry之前就已经创建好了的,并且作为DriverEntry
函数的参数传递给驱动程序。如果驱动程序启动失败,操作
系统将删除该对象。该对象的数据结构如下。注意下表并不是完整地列出了ntddk.h中的DEVICE_OBJECT结构体的所有数
据项,这里仅列出了一般驱动程序可能使用到的数据项。 </FONT></P>
<TABLE width="100%" border=1>
<TBODY>
<TR>
<TD width="50%"><FONT face=宋体>Driver对象数据项 </FONT></TD>
<TD width="50%"><FONT face=宋体>说明 </FONT></TD></TR>
<TR>
<TD width="50%"><FONT face=宋体>PDEVICE_OBJECT DeviceObject </FONT></TD>
<TD width="50%"><FONT face=宋体> 由本驱动程序创建的Device对象的链表</FONT></TD></TR>
<TR>
<TD width="50%"><FONT face=宋体>ULONG Flags
PDRIVER_INITIALIZE DriverInit </FONT></TD>
<TD width="50%"><FONT face=宋体>驱动程序初始化例程(一般较少用) </FONT></TD></TR>
<TR>
<TD width="50%"><FONT face=宋体>PDRIVER_STARTIO DriverStartIo
</FONT></TD>
<TD width="50%"><FONT face=宋体> StartIo例程入口,一般该例程对低层设备驱动程序用得较多,
高层驱动程序较少使用本例程。</FONT></TD></TR>
<TR>
<TD width="50%"><FONT face=宋体>PDRIVER_UNLOAD DriverUnload
</FONT></TD>
<TD width="50%"><FONT
face=宋体>卸载驱动程序例程,如果想在控制面版的设备里停止该设备,应该提供本例程。</FONT></TD></TR>
<TR>
<TD width="50%"><FONT face=宋体>PDRIVER_DISPATCH
MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1] </FONT></TD>
<TD width="50%"><FONT face=宋体> 驱动程序的Dispatch例程表
</FONT></TD></TR></TBODY></TABLE>
<P><FONT face=宋体>在上面提到过驱动程序是管理同类型的所有设备,所以上面的
结构中DeviceObject指向的就不是单个的设备对象,而是一个对象链表,这个链表的维护在下面介绍Device对象时可以看到。 Device对象与Device
Extension 驱动程序在调用IoCreateDevice函数成功后就创建了一个Device
对象。下面对Device对象几个比较重要的数据做一介绍。</FONT></P>
<TABLE width="100%" border=1>
<TBODY>
<TR>
<TD width="25%"><FONT face=宋体>Device对象数据项
</FONT></TD>
<TD width="75%"><FONT face=宋体>说明 </FONT></TD></TR>
<TR>
<TD width="25%"><FONT face=宋体>PVOID DeviceExtension </FONT></TD>
<TD width="75%"><FONT face=宋体>指向Device Extension结构的指针</FONT></TD></TR>
<TR>
<TD width="25%"><FONT face=宋体>PDRIVER_OBJECT DriverObject</FONT></TD>
<TD width="75%"><FONT face=宋体>指向这个设备的Driver对象的指针,IoCreateDevice会
自动填写本数据。</FONT></TD></TR>
<TR>
<TD width="25%"><FONT face=宋体>ULONG Flags </FONT></TD>
<TD width="75%"><FONT face=宋体>指定这个设备的缓冲策略</FONT></TD></TR>
<TR>
<TD width="25%"><FONT face=宋体>PDEVICE_OBJECT NextDevice </FONT></TD>
<TD width="75%"><FONT
face=宋体>指向属于这个驱动程序的下一个设备对象,依靠本数据来维护设备对象链表</FONT></TD></TR>
<TR>
<TD width="25%"><FONT face=宋体>CCHAR StackSize </FONT></TD>
<TD width="75%"><FONT
face=宋体>发送到这个设备的IRP需要的I/O堆栈单元的最小数目,一般对分层驱动程序来说,本数据应该比其下层设备的大1</FONT></TD></TR>
<TR>
<TD width="25%"><FONT face=宋体>ULONG AlignmentRequirement</FONT></TD>
<TD width="75%"><FONT face=宋体>缓冲区要求的内存对齐,一般对分层驱动程序来说,本值应该
和其下层设备的对齐一致</FONT></TD></TR></TBODY></TABLE>
<P><FONT
face=宋体>Device记录着设备的特徵和状态信息,对系统上的每个虚拟的、逻辑的和物理的设备都有一个Device对象。例如对一个硬盘驱动程序,对一个物理硬盘有一个名称为Partition0的Device对象,对应整个物理磁盘,同时对硬盘的每个分区,也都有一个Device对象,它们的名称分别为PartitionX(X从1开始,每个分区对应一个数字)。
<BR>Device Extension是连接到Device对象的一个很重要的数据结构,它的数据结构是由驱动程序设计者自己来确定的,在
调用IoCreateDevice的时候应该指定它的大小,Device Extension其实是由操作系统在非份页内存池中为每个Device
对象分配的一块内存。由于驱动程序必须是完全可重入的, 因此使用任何全局变量和静态变量都不是好的办法,一般来
说和设备有关的任何需要保持的信息都应该放到Device Extension里去。 <BR>设备的缓冲策略也必须提一下,这里的Flag的缓冲策略主要
决定设备读写(功能代码IRP_MJ_READ和IRP_MJ_WRITE)时候的 缓冲策略,另外功能代码IRP_MJ_DEVICE_CONTROL时候的缓冲
策略是由IOCTL控制代码本身来决定的。两者不能混为一谈。 在下面我将专门用一节来讨论I/O的缓冲策略。 <BR>I/O请求包(IRP)
<BR>在上面的结构里面已经出现了IRP了,在这里对它做一说明。
在NT中,几乎所有的I/O都是包驱动的,可以说驱动程序和操作系统其他部份都是通过I/O请求包来进行交互的。我们 来看看一个I/O请求的执行过程。 <BR>(1)
操作系统的I/O管理器从非分页内存分配一个IRP,响应一个I/O请求。基于由客户指定的I/O函数,I/O管理器将该
IRP传递给合适的驱动程序的Dispatch例程。 <BR>(2)
Dispatch例程检查请求的参数是否有效,如果有效,驱动程序根据请求的内容进行一系列的操作。否则设置错 误状态信息直接返回。 <BR>(3)
操作完成时,将数据(如果有)和状态信息存放到IRP中 并返回给I/O管理器。 <BR>(4) I/O管理器对返回的IRP进行适当的处理后将最后状态和
数据(如果有)返回给用户。 <BR><BR>一个IRP的主要数据项如下表所示。 <BR>IRP包括一个IRP头和一个IRP stack
的区域。由于WDM的模式下都是包驱动的,所里IRP可以说是一个非常重要的东东。还有那个该死的URB(God damn URB!)<FONT
color=#c0c0c0>[人一辈子真的很过瘾,有些人或有些事你明明不喜欢或做不来,可是有时侯你又不得不硬着头皮去做,就像一大堆球迷围着一堆狗屎般的中国足球,那班傻儿真是要钱不要脸,丢咱中国人的脸]</FONT><BR></FONT></P>
<TABLE width="100%" border=1>
<TBODY>
<TR>
<TD width="28%"><FONT face=宋体>IRP主要数据项</FONT></TD>
<TD width="72%"><FONT face=宋体>说明 </FONT></TD></TR>
<TR>
<TD width="28%"><FONT face=宋体>IO_STATUS_BLOCK IoStatus</FONT></TD>
<TD width="72%"><FONT face=宋体>存放I/O请求的状态</FONT></TD></TR>
<TR>
<TD width="28%"><FONT face=宋体>PVOID AssociatedIrp.SystemBuffer</FONT></TD>
<TD width="72%"><FONT face=宋体>如果设备执行缓冲I/O,则为指向系统空间缓冲区的指针。
否则为NULL</FONT></TD></TR>
<TR>
<TD width="28%"><FONT face=宋体>PMDL MdlAddress </FONT></TD>
<TD width="72%"><FONT face=宋体>如果设备执行直接I/O,指向用户空间缓冲区的内存描述表的指针</FONT></TD></TR>
<TR>
<TD width="28%"><FONT face=宋体>PVOID UserBuffer</FONT></TD>
<TD width="72%"><FONT face=宋体>I/O缓冲区的用户空间地址 </FONT></TD></TR>
<TR>
<TD width="28%"><FONT face=宋体>BOOLEAN Cancel</FONT></TD>
<TD width="72%"><FONT face=宋体> 指示IRP已被取消
</FONT></TD></TR></TBODY></TABLE>
<P><FONT face=宋体>关于AssociatedIrp.SystemBuffer、MdlAddress和UserBuffer将在
下面的I/O缓冲区策略里面更详细地讨论。
<BR><BR>NT还有更多其他的对象,例如中断对象、Controller对象、定时器对象等等,但在我们开发的驱动程序中并没有用到,因此在这里不做介绍。
<BR>I/O缓冲策略
<BR>很明显的,驱动程序和客户应用程序经常需要进行数据交换,但我们知道驱动程序和客户应用程序可能不在同一个地址空间,因此操作系统必须解决两者之间的数据交换。这就就设计到设备的I/O缓冲策略。<BR>读写请求的I/O缓冲策略
<BR>前面说到通过设置Device对象的Flag可以选择控制处理读写请求的I/O缓冲策略。下面对这些缓冲策略分别做一介绍。
<BR>1、缓冲I/O(DO_BUFFERED_IO)
<BR>在读写请求的一开始,I/O管理器检查用户缓冲区的可访问性,然后分配与调用者的缓冲区一样大的非分页池,并把它的地址放在IRP的AssociatedIrp.SystemBuffer域中。驱动程序就利用这个域来进行实际数据的传输。
<BR>对于IRP_MJ_READ读请求,I/O管理器还把IRP的UserBuffer域设置 成调用者缓冲区的用户空间地址。当请求完成时,I/O管理器利用
这个地址将数据从驱动程序的系统空间拷贝回调用者的缓冲区。对 于IRP_MJ_WRITE写请求,UserBuffer被设置为NULL,并把用户缓冲
区的数据拷贝到系统缓冲区中。 <BR>2、 直接I/O(DO_DIRECT_IO)
<BR>I/O管理器首先检查用户缓冲区的可访问性,并在物理内存中锁定它。然后它为该缓冲区创建一个内存描述表(MDL),并把MDL的地址
存放在IRP的MdlAddress域中。AssociatedIrp.SystemBuffer和 UserBuffer都被设置为NULL。驱动程序可以调用函数
MmGetSystemAddressForMdl得到用户缓冲区的系统空间地址,从而 进行数据操作。这个函数将调用者的缓冲区映射到非份页的地址空
间。驱动程序完成I/O请求后,系统自动从系统空间解除缓冲区的映射。 <BR>3、 这两种方法都不是
<BR>这种情况比较少用,因为这需要驱动程序自己来处理缓冲问题。 I/O管理器仅把调用者缓冲区的用户空间地址放到IRP的UserBuffer
域中。我们并不推荐这种方式。 <BR></FONT></P>
<P><FONT face=宋体>IOCTL缓冲区的缓冲策略 <BR>IOCTL请求涉及来自调用者的输入缓冲区和返回到调用者的输出
缓冲区。为了理解IOCTL请求,我们先来看看WIN32 API DeviceIoControl函数的原型。 <BR>BOOL DeviceIoControl (
<BR>HANDLE hDevice, // 设备句柄 <BR>DWORD dwIoControlCode, // IOCTL请求操作代码 <BR>LPVOID
lpInBuffer, // 输入缓冲区地址 <BR>DWORD nInBufferSize, // 输入缓冲区大小 <BR>LPVOID
lpOutBuffer, // 输出缓冲区地址 <BR>DWORD nOutBufferSize, // 输出缓冲区大小 <BR>LPDWORD
lpBytesReturned, // 存放返回字节数的指针 <BR>LPOVERLAPPED lpOverlapped //
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -