如果图3.4显示一个写请求,I/0管理器将在它发送IRP到驱动程序之前,把从用户 缓冲区拷贝数据到系统缓冲区 对于图3.4中所示的读请求,驱动程序将数据从属设备读到系统空间缓冲区。当读的 请求被满足,驱动程序使用IRP调用 IoCompleteRequest 6.当原来的线程再一次成为当前的线程,I/0管理器从系统缓冲区将读入数据拷贝到用 户缓冲区。它也调用 ExFreePool以释放系统缓冲区。 在Ⅰ/0管理器已经为驱动程序创建系统空间缓冲区之后,请求的用户模式线程可能被交换 出去,而其物理内存可能被另一个线程所重用,并且这个线程可能是属于另一个过程的 然而,直到驱动程序使用IRP调用 loCompleteRequest,在IRP中所提供的系统空间虚拟 地址范围会一直保持有效 次不传输大量数据的设备(例如交互式的设备)的驱动程序能使用缓冲I/0。 次传输大量数据的驱动程序,尤其是做多页传输的驱动程序,不应该试图使用缓冲 Ⅰ/O。当系统运行时,非页式存储池可能变成碎片,这样I/O管理器不能分配大而连续的系 统空间缓冲区给这样的驱动程序来发送IRP。 注意,对于某种 IRP MJ XXX,所有驱动程序使用缓冲I/0。甚至,为直接I/0而创建它们 自己的设备对象的驱动程序会为大多数请求使用缓冲I/0,这些请求不包括 IRP MJ READ IRP MJ WRITE,还可能不包括驱动程序定义的 IRP MJ INTERNAL DEVICE CONTROL请求,这 个请求要求大数据传输。设备控制请求的缓冲方法由这个设备控制编码自己来确定。 3242使用直接lo 图3.5说明Ⅰ⑩0管理器如何创建一个向驱动程序请求传输操作的IRP,这个驱动程序使用 00 DIRECT I0与它们的设备对象的 Flags作“或”操作。 图3.5使用DMA的设备的用户缓冲区上的直接I/0 图3.5说明驱动程序如何使用IRP的 MdlAddress来为读请求传递数据。图中的驱动程序使 用基于包的系统或者总线控制器DMA,并且已经使用 DO DIRECT I0对设备对象的 Flags作 或”操作 1.用户空间的虚拟地址的某些范围代表当前线程的缓冲区,并且缓冲区内容实际上可以 被存储在一些物理上不连续的页上(图3.5中黑暗部分)。I/0管理器创建MDL描述 这个缓冲区。MDL是一个不透明的由内存管理器定义的数据结构,这个数据结构映射 块特殊的虚拟地址范围到一个或多个基于页的物理地址范围 2.I/0管理器为当前线程的读请求服务,线程为它传递代表缓冲区的用户空间虚拟地址 的范围。 3.I/0管理器或者FSD检查用户提供的缓冲区的可访问性,并且调用使用前面创建的MDL 调用 MmProbeAndLockPageso MmProbeAndLockPages也填充MDL中相应的物理地址范 围。 如图3.5所示,虚拟范围的MDL可能有几个相应的基于页的物理地址入口和虚拟范 围,因为缓冲区可以在距ML中描述的首页和末页的开始一些字节偏移的地方开始和 结束 4.I/0管理器在IRP中提供一个指向MDL( MdlAddress)的指针。直到驱动程序完成IRP 后Ⅰ/0管理器或者文件系统调用 MmUnlockPages,在MDL中所描述的物理页仍然锁定 并被分配到缓冲区。然而,这样的MDL中的虚拟地址能变得不可见(和无效),即使
36 如果图 3.4 显示一个写请求,I/O 管理器将在它发送 IRP 到驱动程序之前,把从用户 缓冲区拷贝数据到系统缓冲区。 5. 对于图 3.4 中所示的读请求,驱动程序将数据从属设备读到系统空间缓冲区。当读的 请求被满足,驱动程序使用 IRP 调用 IoCompleteRequest。 6. 当原来的线程再一次成为当前的线程,I/O 管理器从系统缓冲区将读入数据拷贝到用 户缓冲区。它也调用 ExFreePool 以释放系统缓冲区。 在 I/O 管理器已经为驱动程序创建系统空间缓冲区之后,请求的用户模式线程可能被交换 出去,而其物理内存可能被另一个线程所重用,并且这个线程可能是属于另一个过程的。 然而,直到驱动程序使用 IRP 调用 IoCompleteRequest,在 IRP 中所提供的系统空间虚拟 地址范围会一直保持有效。 一次不传输大量数据的设备(例如交互式的设备)的驱动程序能使用缓冲 I/O。 一次传输大量数据的驱动程序,尤其是做多页传输的驱动程序,不应该试图使用缓冲 I/O。当系统运行时,非页式存储池可能变成碎片,这样 I/O 管理器不能分配大而连续的系 统空间缓冲区给这样的驱动程序来发送 IRP。 注意,对于某种 IRP_MJ_XXX,所有驱动程序使用缓冲 I/O。甚至,为直接 I/O 而创建它们 自己的设备对象的驱动程序会为大多数请求使用缓冲 I/O,这些请求不包括 IRP_MJ_READ、 IRP_MJ_WRITE,还可能不包括驱动程序定义的 IRP_MJ_INTERNAL_DEVICE_CONTROL 请求,这 个请求要求大数据传输。 设备控制请求的缓冲方法由这个设备控制编码自己来确定。 3.2.4.2 使用直接 I/O 图 3.5 说明 I/O 管理器如何创建一个向驱动程序请求传输操作的 IRP,这个驱动程序使用 DO_DIRECT_IO 与它们的设备对象的 Flags 作“或”操作。 图 3.5 使用 DMA 的设备的用户缓冲区上的直接 I/O 图 3.5 说明驱动程序如何使用 IRP 的 MdlAddress 来为读请求传递数据。图中的驱动程序使 用基于包的系统或者总线控制器 DMA,并且已经使用 DO_DIRECT_IO 对设备对象的 Flags 作 “或”操作。 1. 用户空间的虚拟地址的某些范围代表当前线程的缓冲区,并且缓冲区内容实际上可以 被存储在一些物理上不连续的页上(图 3.5 中黑暗部分)。I/O 管理器创建 MDL 描述 这个缓冲区。MDL 是一个不透明的由内存管理器定义的数据结构,这个数据结构映射 一块特殊的虚拟地址范围到一个或多个基于页的物理地址范围。 2. I/O 管理器为当前线程的读请求服务,线程为它传递代表缓冲区的用户空间虚拟地址 的范围。 3. I/O 管理器或者 FSD 检查用户提供的缓冲区的可访问性,并且调用使用前面创建的 MDL 调用 MmProbeAndLockPages。MmProbeAndLockPages 也填充 MDL 中相应的物理地址范 围。 如图 3.5 所示,虚拟范围的 MDL 可能有几个相应的基于页的物理地址入口和虚拟范 围,因为缓冲区可以在距 MDL 中描述的首页和末页的开始一些字节偏移的地方开始和 结束。 4. I/O 管理器在 IRP 中提供一个指向 MDL(MdlAddress)的指针。直到驱动程序完成 IRP 后 I/O 管理器或者文件系统调用 MmUnlockPages,在 MDL 中所描述的物理页仍然锁定 并被分配到缓冲区。然而,这样的 MDL 中的虚拟地址能变得不可见(和无效),即使
是在IRP被发送到设备驱动程序或者发送到可能在设备驱动程序之上的任何中间层驱 动程序之前。 如果驱动程序使用基于包的系统或者总线控制器DMA,它使用IRP的 MaPAddress指针 调用 MmGetMdlvirtualAddress,以得到MDL基于页的入口的基本虚拟地址 6.当驱动程序的 AdapterControl例程拥有到DMA通道的访问和/或映射驱动器,为了从 设备中直接读取数据到物理内存,驱动程序使用由 MmGetMdlvirtualaddress返回的基 地址调用 Maptransfer 驱动程序应该总是检査缓冲区长度。注意,I/0管理器不能为零长度的缓冲创建MDL 在DMA和PI0传输期间,驱动程序必须拥有措施保持高速缓存的一致性。使用DMA的驱动 程序也必须使用适配器对象 使用可编程I/0(P10)而不是DMA的设备必须两次映射用户空间缓冲区到系统空间地址范 围,如图3.6所示。 图3.6使用PIO的设备的直接I/0 图3.6显示使用PI0的设备如何处理同样的任务。 1.用户空间虚拟地址的某些范围代表当前线程的缓冲区,并且缓冲区的内容实际上可以被 存储在一些物理上不连续的页上。如果缓冲区长度是零,I/0管理器创建MDL描述这个 缓冲区 2.I/0管理器为当前线程的读请求服务,线程为它传递代表缓冲区的用户空间虚拟地址的 范围。 3.I/0管理器或者FSD检査用户提供的缓冲区的可访问性。如果I/0管理器已创建ML, 它使用ML调用 MmProbeAndLockPages,这个MDL为用户缓冲区指定了虚拟地址的范 围。 MmProbeAndLockPages也填充ML中相应的物理地址范围。 4.I/O管理器在请求传输操作的IRP中提供一个指向ML( MdlAddress)的指针。直到驱 动程序完成IRP后1/0管理器或者文件系统调用 MmUnlockPages,在MDL中所描述的物 理页仍然保持锁定并被分配到缓冲区。然而,这样的ML中的虚拟地址能变得不可见 (和无效),即使是在IRP被发送到设备驱动程序或者发送到可能在设备驱动程序之上 的任何中间层驱动程序之前 5.如果驱动程序请求系统(虚拟的)地址,驱动程序使用IRP的 MdlAddress指针调用 MmGet SystemAddressForMdlSafe(Windows 2000) E MmGetSystemAddressForMdl (wDM),以两次映射MD中的用户空间虚拟地址到系统空间地址范围。在图3.6中, AliasBuff代表描述双重映射的地址的ML 6.驱动程序使用来自双重映射的MDL的系统空间虚拟的地址范围( AliasBuff),将数据 读到内存中 当驱动程序通过调用 IoCompleteRequest完成IRP,如果驱动程序调用 MmGetSystemAddressForMdlSafe(Windows 2000)EX* MmGet SystemAddressForMdl (wDM)的话,I/0管理器或者文件系统释放MDL的双重映射的系统空间范围。 Ⅰ/o管理器或者文件系统解锁在ML中描述的页,并且根据驱动程序的需要处置ML和 IRP。要想得到较好的性能,驱动程序应该避免双重映射ML物理地址到系统空间,如在步 骤3中描述的,除非它们必须使用虚拟的地址。这样做没有必要使用系统页入口,并能降 低驱动程序性能和伸缩性。此外,如果它运行出页表入口,系统可能崩溃,因为大多数较 老的驱动程序不能处理这种情况
37 是在 IRP 被发送到设备驱动程序或者发送到可能在设备驱动程序之上的任何中间层驱 动程序之前。 5. 如果驱动程序使用基于包的系统或者总线控制器 DMA,它使用 IRP 的 MdIAddress 指针 调用 MmGetMdlVirtualAddress,以得到 MDL 基于页的入口的基本虚拟地址。 6. 当驱动程序的 AdapterControl 例程拥有到 DMA 通道的访问和/或映射驱动器,为了从 设备中直接读取数据到物理内存,驱动程序使用由 MmGetMdlVirtualAddress 返回的基 地址调用 MapTransfer。 驱动程序应该总是检查缓冲区长度。注意,I/O 管理器不能为零长度的缓冲创建 MDL。 在 DMA 和 PIO 传输期间,驱动程序必须拥有措施保持高速缓存的一致性。使用 DMA 的驱动 程序也必须使用适配器对象。 使用可编程 I/O(PlO)而不是 DMA 的设备必须两次映射用户空间缓冲区到系统空间地址范 围,如图 3.6 所示。 图 3.6 使用 PIO 的设备的直接 I/O 图 3.6 显示使用 PIO 的设备如何处理同样的任务。 1. 用户空间虚拟地址的某些范围代表当前线程的缓冲区,并且缓冲区的内容实际上可以被 存储在一些物理上不连续的页上。如果缓冲区长度是零,I/O 管理器创建 MDL 描述这个 缓冲区。 2. I/O 管理器为当前线程的读请求服务,线程为它传递代表缓冲区的用户空间虚拟地址的 范围。 3. I/O 管理器或者 FSD 检查用户提供的缓冲区的可访问性。如果 I/O 管理器已创建 MDL, 它使用 MDL 调用 MmProbeAndLockPages,这个 MDL 为用户缓冲区指定了虚拟地址的范 围。 MmProbeAndLockPages 也填充 MDL 中相应的物理地址范围。 4. I/O 管理器在请求传输操作的 IRP 中提供一个指向 MDL(MdlAddress)的指针。直到驱 动程序完成 IRP 后 I/O 管理器或者文件系统调用 MmUnlockPages,在 MDL 中所描述的物 理页仍然保持锁定并被分配到缓冲区。然而,这样的 MDL 中的虚拟地址能变得不可见 (和无效),即使是在 IRP 被发送到设备驱动程序或者发送到可能在设备驱动程序之上 的任何中间层驱动程序之前。 5. 如果驱动程序请求系统(虚拟的)地址,驱动程序使用 IRP 的 MdlAddress 指针调用 MmGetSystemAddressForMdlSafe(Windows 2000)或 MmGetSystemAddressForMdl (WDM),以两次映射 MDL 中的用户空间虚拟地址到系统空间地址范围。在图 3.6 中, AliasBuff 代表描述双重映射的地址的 MDL。 6. 驱动程序使用来自双重映射的 MDL 的系统空间虚拟的地址范围(AliasBuff),将数据 读到内存中。 当驱动程序通过调用 IoCompleteRequest 完成 IRP,如果驱动程序调用 MmGetSystemAddressForMdlSafe(Windows 2000)或者 MmGetSystemAddressForMdl (WDM)的话,I/O 管理器或者文件系统释放 MDL 的双重映射的系统空间范围。 I/O 管理器或者文件系统解锁在 MDL 中描述的页,并且根据驱动程序的需要处置 MDL 和 IRP。要想得到较好的性能,驱动程序应该避免双重映射 MDL 物理地址到系统空间,如在步 骤 3 中描述的,除非它们必须使用虚拟的地址。这样做没有必要使用系统页入口,并能降 低驱动程序性能和伸缩性。此外,如果它运行出页表入口,系统可能崩溃,因为大多数较 老的驱动程序不能处理这种情况
仅仅在那个线程是当前的时候,当前用户线程的缓冲区和线程本身被保证常驻在物理内存 中。对于在图3.6中所显示的线程,当另一个过程的线程被运行时,其用户缓冲区的内容 可能被换到二级存储中。当另一个过程的线程被运行时,请求的线程的缓冲区的系统物理 内存能被写,除非内存管理器已经锁定并且保存包含原来线程的缓冲区的相应物理页 然而,当另一个线程当前正在运行的时候,其缓冲区的原始线程的虚拟地址并不保持是可 见的,即使内存管理器保存缓冲区的物理页。因而,驱动程序不能使用由 MmGetMdlvirtualaddress返回的一个虚拟地址来访问内存。为了使用基于包的系统或者总 线控制器DMA传输数据,这个例程的调用者必须把其结果传递到 Maptransfer(与IRP的 MdlAddress指针一同)。 3243使用非直接也非缓冲的l/O 如果设备对象没有被创建使用直接和缓冲I/0,那么直接I/0管理器将传递原始用户空间 虚拟地址,这个地址在被发送到驱动程序的IRP中。因为这样一种驱动程序必须在调用的 线程的上下文中执行,以安全地访问其缓冲区,仅仅最高层驱动程序(例如FSD)能创建 它们的设备对象,而不用使用 DO DIRECT I0或者 DO BUFFERED IO与每个设备对象的 Flags作“或”操作。 中间或者最低层驱动程序不能总满足这个条件。例如,如果一个正在请求的线程等待一个 Ⅰ/O请求的完成,或者如果一个较高层驱动程序(特别是文件系统)位于中间层或者最低 层驱动程序之上,那么较低层驱动程序的例程是不可能在请求的线程的上下文中被调用 的 当I/0管理器为缓冲区发送带有当前线程的用户空间虚拟地址的IRP时,没有使用 D0 BUFFERED I0或者 DO DIRECT I0与设备对象的 Flags作“或”操作的驱动程序必须作 下列事情 1.检査用户缓冲区的地址范围的有效性,并检査是否适当的读或写被许可,这个可以用 Probe Forhead和 Probe ForWrite支持例程。驱动程序必须将它对缓冲区的地址范围的 访问封入驱动程序提供的例外情况处理者中,从而使得在这个驱动程序访问内存时用户 线程不能改变对缓冲区的访问权限。如果检测到发生了例外情况,驱动程序将返回错 误。驱动程序必须调用作1/0请求的线程的上下文之内的例程;因此,只有较高层驱动 程序能执行这种任务。 2.以下列的方法之一管理缓冲区和内存操作: ■完成它自己的双缓冲区操作,就像I/0管理器为使用缓冲I/0的驱动程序做的那 样 ■创建它自己MDL,并且通过调用内存管理器的支持例程锁定缓冲区,就像I/0管理 器为使用直接I/0的驱动程序做的那样 在调用的线程的上下文中的用户缓冲区上直接执行所有必要的操作。驱动程序必须 在驱动程序提供的例外处理器中封装它对缓冲区的访问,以防在驱动程序访问内存 时,用户线程改变访问缓冲区的权限,或者改变缓冲区中的数据 实际上,驱动程序必须在每IRP基础上选择是否做缓冲I/0、直接I/0、或者在调用线程的 上下文中的I/0,并且它必须处理可能在用户模式线程上下文中发生的任何例外情况。驱 动程序必须管理它自己的用户缓冲区访问、双缓冲区操作、和内存映射,当必要时,让 Ⅰ/O管理器代替为驱动程序处理这些操作
38 仅仅在那个线程是当前的时候,当前用户线程的缓冲区和线程本身被保证常驻在物理内存 中。对于在图 3.6 中所显示的线程,当另一个过程的线程被运行时,其用户缓冲区的内容 可能被换到二级存储中。当另一个过程的线程被运行时,请求的线程的缓冲区的系统物理 内存能被写,除非内存管理器已经锁定并且保存包含原来线程的缓冲区的相应物理页。 然而,当另一个线程当前正在运行的时候,其缓冲区的原始线程的虚拟地址并不保持是可 见的,即使内存管理器保存缓冲区的物理页。因而,驱动程序不能使用由 MmGetMdlVirtualAddress 返回的一个虚拟地址来访问内存。为了使用基于包的系统或者总 线控制器 DMA 传输数据,这个例程的调用者必须把其结果传递到 MapTransfer(与 IRP 的 MdlAddress 指针一同)。 3.2.4.3 使用非直接也非缓冲的 I/O 如果设备对象没有被创建使用直接和缓冲 I/O,那么直接 I/O 管理器将传递原始用户空间 虚拟地址,这个地址在被发送到驱动程序的 IRP 中。因为这样一种驱动程序必须在调用的 线程的上下文中执行,以安全地访问其缓冲区,仅仅最高层驱动程序(例如 FSD)能创建 它们的设备对象,而不用使用 DO_DIRECT_IO 或者 DO_BUFFERED_IO 与每个设备对象的 Flags 作“或”操作。 中间或者最低层驱动程序不能总满足这个条件。例如,如果一个正在请求的线程等待一个 I/O 请求的完成,或者如果一个较高层驱动程序(特别是文件系统)位于中间层或者最低 层驱动程序之上,那么较低层驱动程序的例程是不可能在请求的线程的上下文中被调用 的。 当 I/O 管理器为缓冲区发送带有当前线程的用户空间虚拟地址的 IRP 时,没有使用 DO_BUFFERED_IO 或者 DO_DIRECT_IO 与设备对象的 Flags 作“或”操作的驱动程序必须作 下列事情: 1. 检查用户缓冲区的地址范围的有效性,并检查是否适当的读或写被许可,这个可以用 ProbeForRead 和 ProbeForWrite 支持例程。驱动程序必须将它对缓冲区的地址范围的 访问封入驱动程序提供的例外情况处理者中,从而使得在这个驱动程序访问内存时用户 线程不能改变对缓冲区的访问权限。如果检测到发生了例外情况,驱动程序将返回错 误。驱动程序必须调用作 I/O 请求的线程的上下文之内的例程;因此,只有较高层驱动 程序能执行这种任务。 2. 以下列的方法之一管理缓冲区和内存操作: ▪ 完成它自己的双缓冲区操作,就像 I/O 管理器为使用缓冲 I/O 的驱动程序做的那 样。 ▪ 创建它自己 MDL,并且通过调用内存管理器的支持例程锁定缓冲区,就像 I/O 管理 器为使用直接 I/O 的驱动程序做的那样。 ▪ 在调用的线程的上下文中的用户缓冲区上直接执行所有必要的操作。驱动程序必须 在驱动程序提供的例外处理器中封装它对缓冲区的访问,以防在驱动程序访问内存 时,用户线程改变访问缓冲区的权限,或者改变缓冲区中的数据。 实际上,驱动程序必须在每 IRP 基础上选择是否做缓冲 I/O、直接 I/O、或者在调用线程的 上下文中的 I/O,并且它必须处理可能在用户模式线程上下文中发生的任何例外情况。驱 动程序必须管理它自己的用户缓冲区访问、双缓冲区操作、和内存映射,当必要时,让 I/O 管理器代替为驱动程序处理这些操作
33适配器对象和DMA 如果驱动程序为直接I/0创建它的设备对象,并且它的设备使用DMA,则它必须使用系统 创建的适配器对象,如在3.2.4.2中论述的那样。适配器对象代表DMA控制器通道或者端 口,或者总线控制器设备。 两种最低层驱动程序必须使用适配器对象 使用系统DMA控制器(也称为从属设备)的设备的驱动程序。这样的设备被称为“使 用系统DMA”。 使用系统DMA控制器的从属DMA设备的驱动程序必须有 AdapterControl例程,并且 调用系统提供的支持例程,这些支持例程为了完成DMA传输而操作适配器对象。细节 参见“使用系统DMA”。 是总线控制器适配器的设备的驱动程序。这样的设备为I/O总线的使用进行系统仲 裁,然后使用总线控制器DMA。细节参见“使用总线控制器DMA” 非SCSI的总线控制器DMA设备的驱动程序通常必须有 Adapter Control例程,并且必 须调用系统提供的支持例程,这些支持例程为了完成DMA传输而操纵适配器对象 SCSI端口驱动程序为HBA特定的SCSI微端口驱动程序创建所有必要的适配器对象 当微端口驱动程序的 HwScsiFindAdapter例程执行时,微端口驱动程序为SCSI端口驱 动程序提供必要的数据,以创建相应的适配器对象。 简短地讲,控制总线控制器设备或连接到系统DMA控制器的从属设备的任何最低层驱动程 序,必须明确地使用适配器对象(参见“获取适配器对象”)关联它的设备对象。驱动程 序为指向适配器对象的指针提供存储,通常是在设备扩展中。此外,驱动程序拥有一个 Adapter Control例程,这个例程调用系统提供的适配器对象支持例程以执行DMA传输 作为设备启动的一部分,这些驱动程序调用I/O管理器,I/0管理器轮流调用平台特定的 HAL来创建一组适配器对象。这样,在任何 Windows平台上,适配器对象组通常为下述东 西包括一个适配器对象: 每个系统DMA控制器通道或者端口,从属设备被附加到这个端口。 机器中的每个总线控制器DMA设备。 除使用适配器对象之外,执行DMA的驱动程序使用三种不同的地址空间,如图3.7中所 图3.7物理、逻辑、和虚拟的地址映射 在任何 Windows平台上,驱动程序拥有对处理器支持全部虚拟地址空间的访问。在32位 处理器上,虚拟地址空间可以描述四个G( gigabyte)的空间。CPU用页表的方法将虚拟地 址空间中的地址转换为系统物理地址空间中的地址。每个页表项(PTE)映射虚拟内存的 页到物理内存的一页,必要时作翻页操作。ML(内存描述符列表)为与驱动程序DMA操作 有关的缓冲区提供类似的映射 设备根据它们的能力改变它们对系统所有虚拟地址空间的访问。设备在逻辑(设备)地址 空间中使用地址。每个HAL使用映射寄存器把设备或者逻辑地址转换成为一个物理地址 (物理RAM中的位置)。对于设备硬件,映射寄存器执行与ML(和页表)为软件(驱动 程序)执行的相同的功能:它们把地址转换成为物理的内存
39 3.3 适配器对象和 DMA 如果驱动程序为直接 I/O 创建它的设备对象,并且它的设备使用 DMA,则它必须使用系统 创建的适配器对象,如在 3.2.4.2 中论述的那样。适配器对象代表 DMA 控制器通道或者端 口,或者总线控制器设备。 两种最低层驱动程序必须使用适配器对象: ▪ 使用系统 DMA 控制器(也称为从属设备)的设备的驱动程序。这样的设备被称为“使 用系统 DMA” 。 使用系统 DMA 控制器的从属 DMA 设备的驱动程序必须有 AdapterControl 例程,并且 调用系统提供的支持例程,这些支持例程为了完成 DMA 传输而操作适配器对象。细节 参见“使用系统 DMA”。 ▪ 是总线控制器适配器的设备的驱动程序。这样的设备为 I/O 总线的使用进行系统仲 裁,然后使用总线控制器 DMA。细节参见“使用总线控制器 DMA”。 非 SCSI 的总线控制器 DMA 设备的驱动程序通常必须有 AdapterControl 例程,并且必 须调用系统提供的支持例程,这些支持例程为了完成 DMA 传输而操纵适配器对象。 SCSI 端口驱动程序为 HBA 特定的 SCSI 微端口驱动程序创建所有必要的适配器对象。 当微端口驱动程序的 HwScsiFindAdapter 例程执行时,微端口驱动程序为 SCSI 端口驱 动程序提供必要的数据,以创建相应的适配器对象。 简短地讲,控制总线控制器设备或连接到系统 DMA 控制器的从属设备的任何最低层驱动程 序,必须明确地使用适配器对象(参见“获取适配器对象”)关联它的设备对象。驱动程 序为指向适配器对象的指针提供存储,通常是在设备扩展中。此外,驱动程序拥有一个 AdapterControl 例程,这个例程调用系统提供的适配器对象支持例程以执行 DMA 传输。 作为设备启动的一部分,这些驱动程序调用 I/O 管理器,I/O 管理器轮流调用平台特定的 HAL 来创建一组适配器对象。这样,在任何 Windows 平台上,适配器对象组通常为下述东 西包括一个适配器对象: ▪ 每个系统 DMA 控制器通道或者端口,从属设备被附加到这个端口。 ▪ 机器中的每个总线控制器 DMA 设备。 除使用适配器对象之外,执行 DMA 的驱动程序使用三种不同的地址空间,如图 3.7 中所 示。 图 3.7 物理、逻辑、和虚拟的地址映射 在任何 Windows 平台上,驱动程序拥有对处理器支持全部虚拟地址空间的访问。在 32 位 处理器上,虚拟地址空间可以描述四个 G(gigabyte)的空间。CPU 用页表的方法将虚拟地 址空间中的地址转换为系统物理地址空间中的地址。每个页表项(PTE)映射虚拟内存的一 页到物理内存的一页,必要时作翻页操作。MDL(内存描述符列表)为与驱动程序 DMA 操作 有关的缓冲区提供类似的映射。 设备根据它们的能力改变它们对系统所有虚拟地址空间的访问。设备在逻辑(设备)地址 空间中使用地址。每个 HAL 使用映射寄存器把设备或者逻辑地址转换成为一个物理地址 (物理 RAM 中的位置)。对于设备硬件,映射寄存器执行与 MDL(和页表)为软件(驱动 程序)执行的相同的功能:它们把地址转换成为物理的内存
因为这些地址被分别编址,所以驱动程序不能在虚拟地址空间中使用指针在物理内存中的 定位一个位置,反之亦然。驱动程序必须首先把虚拟的地址转换成为一个物理的地址。同 样地,驱动程序不能使用一个逻辑地址来访问物理内存;它必须首先转换地址。 3.3.1映射寄存器 HAL必须创建支持不同机器上的各种各样的DMA设备和I/O总线DMA的适配器对象。例 如,大多数 ISA DMA控制器、从属设备、和总线控制器设备没有足够地址线以访问32位处 理器的全部四G系统物理地址空间。相比之下, EISA DMA设备一般有多于足够数量的地址 线在32位处理器中访问全部的系统物理地址空间。因而,所有的HAL都提供映射,这些映 射将DMA设备能访问的逻辑地址范围映射到每台单独机器的物理地址范围 每个适配器对象与一个或多个映射寄存器有关,依赖于将要传递的数据的数量和可供使用 的内存的数量。在DMA传输期间,HAL使用每个映射寄存器用设备可访问的逻辑页作为CPU 中物理内存页的别名。实际上,映射寄存器为使用DMA的驱动程序提供发散/收集支持,而 不管它们的设备是否有发散/收集能力 图3.8说明这样一种物理-逻辑地址的映射,它是没有分散和收集能力的 ISA DMA设备的驱 动程序的 图3.8样本 ISA DMA设备的地址映射 图3.8显示了下列类型的映射 1.每个映射寄存器为 ISA DMA设备映射物理地址(实线所指的)的范围到低级逻辑地址 (虚线) 在这里,三个映射寄存器被用于将系统物理内存中的三个分页的范围别称为 ISA DMA 设备的低级逻辑地址 2.在DMA操作期间,ISA设备使用绘制映射的逻辑地址来访问系统内存。 对于一种可比较的 EISA DMA设备,三个映射寄存器也将用于数据的三个页大小的范 围。然而,映射的逻辑地址范围将没有必要与相应的物理地址范围相同,所以EISA设 备也将使用逻辑地址来访问系统内存。 3.每个MDL中的项映射虚拟地址空间中的位置到物理地址。 注意映射寄存器与MDL中虚拟-物理项之间的对应 对一个DMA传输操作,每个映射寄存器和ML中的每个虚拟项映射至少一个完整的数 据物理页 每个映射寄存器和MDL中的每个虚拟项可以映射小于一个完整的数据物理页。例如, ML中的初始虚拟项可能从物理页边界映射一个偏移量,如图3.5所示 每个映射寄存器和MDL中的每个虚拟项映射最小是一个字节。 在IRP中请求读或写操作,在Irp-> MdlAddress的对驱动程序不透明的MDL中的每个 虚拟项代表用户缓冲区的物理内存中的一个页边界。同样地,单独的DMA传输需要的 每个额外映射寄存器代表设备可访问的逻辑地址范围中的页边界,这个逻辑地址是对 系统物理地址的别称 在所有的 Windows平台上,每个适配器对象拥有一个相关的集合,这个集合由一个或多个 位于平台特定的(和对驱动程序不透明的)基地址上的映射寄存器组成。从驱动程序的观 点来说,在图3.9中所显示的 MapRegisterBase是一组映射寄存器的句柄,这些映射寄存
40 因为这些地址被分别编址,所以驱动程序不能在虚拟地址空间中使用指针在物理内存中的 定位一个位置,反之亦然。驱动程序必须首先把虚拟的地址转换成为一个物理的地址。 同 样地,驱动程序不能使用一个逻辑地址来访问物理内存; 它必须首先转换地址。 3.3.1 映射寄存器 HAL 必须创建支持不同机器上的各种各样的 DMA 设备和 I/O 总线 DMA 的适配器对象。例 如,大多数 ISA DMA 控制器、从属设备、和总线控制器设备没有足够地址线以访问 32 位处 理器的全部四 G 系统物理地址空间。相比之下,EISA DMA 设备一般有多于足够数量的地址 线在 32 位处理器中访问全部的系统物理地址空间。因而,所有的 HAL 都提供映射,这些映 射将 DMA 设备能访问的逻辑地址范围映射到每台单独机器的物理地址范围。 每个适配器对象与一个或多个映射寄存器有关,依赖于将要传递的数据的数量和可供使用 的内存的数量。在 DMA 传输期间,HAL 使用每个映射寄存器用设备可访问的逻辑页作为 CPU 中物理内存页的别名。实际上,映射寄存器为使用 DMA 的驱动程序提供发散/收集支持,而 不管它们的设备是否有发散/收集能力。 图 3.8 说明这样一种物理-逻辑地址的映射,它是没有分散和收集能力的 ISA DMA 设备的驱 动程序的。 图 3.8 样本 ISA DMA 设备的地址映射 图 3.8 显示了下列类型的映射: 1. 每个映射寄存器为 ISA DMA 设备映射物理地址(实线所指的)的范围到低级逻辑地址 (虚线)。 在这里,三个映射寄存器被用于将系统物理内存中的三个分页的范围别称为 ISA DMA 设备的低级逻辑地址。 2. 在 DMA 操作期间,ISA 设备使用绘制映射的逻辑地址来访问系统内存。 对于一种可比较的 EISA DMA 设备,三个映射寄存器也将用于数据的三个页大小的范 围。然而,映射的逻辑地址范围将没有必要与相应的物理地址范围相同,所以 EISA 设 备也将使用逻辑地址来访问系统内存。 3. 每个 MDL 中的项映射虚拟地址空间中的位置到物理地址。 注意映射寄存器与 MDL 中虚拟-物理项之间的对应: ▪ 对一个 DMA 传输操作,每个映射寄存器和 MDL 中的每个虚拟项映射至少一个完整的数 据物理页。 ▪ 每个映射寄存器和 MDL 中的每个虚拟项可以映射小于一个完整的数据物理页。例如, MDL 中的初始虚拟项可能从物理页边界映射一个偏移量,如图 3.5 所示。 ▪ 每个映射寄存器和 MDL 中的每个虚拟项映射最小是一个字节。 ▪ 在 IRP 中请求读或写操作,在 Irp->MdlAddress 的对驱动程序不透明的 MDL 中的每个 虚拟项代表用户缓冲区的物理内存中的一个页边界。同样地,单独的 DMA 传输需要的 每个额外映射寄存器代表设备可访问的逻辑地址范围中的页边界,这个逻辑地址是对 系统物理地址的别称。 在所有的 Windows 平台上,每个适配器对象拥有一个相关的集合,这个集合由一个或多个 位于平台特定的(和对驱动程序不透明的)基地址上的映射寄存器组成。从驱动程序的观 点来说,在图 3.9 中所显示的 MapRegisterBase 是一组映射寄存器的句柄,这些映射寄存