在用户模式与核心模式之间共享内存的最简单有效的方法是使用 IOCTL。IOCTL 有四种不同的类型,下列三种 IOCTL 使您可以在设备驱动程序中直接访问用户缓冲区:
METHOD_IN_DIRECT
METHOD_OUT_DIRECT
METHOD_NEITHER
在以上这些方法中没有创建中间系统缓冲区。
METHOD_IN_DIRECT 和 METHOD_OUT_DIRECT 自动在 DeviceIoControl 中锁定用户指定的缓冲区,并为驱动程序创建内存描述列表 (MDL)。驱动程序可以从 MDL 得到一个系统地址,并根据 IOCTL 传输类型将信息传入传出缓冲区。由于所有的缓冲区验证、锁定及 MDL 创建均是通过 I/O 管理器来完成的,所以这是一种简单的方法。
相反,METHOD_NEITHER 直接将用户缓冲区提供给驱动程序,驱动程序必须适当地验证缓冲区并锁定缓冲区,如果需要,还要为缓冲区获取一个系统地址。虽然这是通过设备驱动程序 I/O 子系统的最快捷路径,但使用这种方法时也有一些应当注意的问题。有关其它信息,请参见 Microsoft Knowledge Base 中的下列文章:
Q126416 INFO:使用 METHOD_NEITHER IOCTL 的警告
要了解这三种不同的 IOCTL 如何工作,请参见以下 Microsoft Knowledge Base 文章:
Q178317 文件:IOCTL.exe:如何使用不同类型的 IOCTL
MmMapLockedPages 方法:设备驱动程序分配局模斗共享内存
在这种方法中,驱动程序通过 MmAllocateContiguousMemory 或 ExAllocatePoolXxx 函数分配内存,创建并建立描述缓冲区的 MDL,桐磨并使用 MmMapLockedPages 把内存映射到用户进程地址空间中。用户应用程序可以使用由 MmMapLockPages 返回的虚拟地址直接访问系统内存。
由于应当在访问内存的进程上下文中完成映射,所以此方法只能用于单层结构的驱动程序,这时可以保证 dispatch 例程在调用进程上下文中运行。您可以在任意数量的用户过程地址空间上映射同一个系统缓冲区。不过,应当设计一种保护机制,使驱动程序及所有应用程序对内存的访问同步。进一步说,应当在终止进程之前或者一旦使用完缓冲区就在映码脊射缓冲区的相同进程上下文中取消缓冲区的映射。下面介绍将驱动程序缓冲区映射至某个用户进程时需采取的步骤:
首先如下分配内存:
SystemVirtualAddress = MmAllocateContiguousMemory(NumberOfBytes,
HighestAcceptableAddress)或
SystemVirtualAddress = ExAllocatePool(PoolType, NumberOfBytes)
如下分配一个 MDL:
Mdl = IoAllocateMdl(SystemVirtualAddress, NumberOfBytes, FALSE,
FALSE, NULL)
建立 MDL 以描述内存页。如果已经分配了非页面缓冲池中的内存,请使用:
MmBuildMdlForNonPagedPool(Mdl)
如果分配了页面缓冲池中的内存,请使用:
MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess)
如下将锁定的页面映射至进程的用户地址空间中:
UserVirtualAddress = MmMapLockedPages(Mdl, UserMode)
前三步可以在 DriverEntry 或 Dispatch 例程中完成。但是,将缓冲区映射至用户地址空间的最后一步,应当在运行于调用进程上下文的某个例程(通常是单层驱动程序的 dispatch 例程)中完成。
您可以在某个提高的 IRQL 级及任何进程上下文(即中断服务例程 (ISR) 及延迟过程调用 (DPC))的驱动程序中使用 SystemVirtualAddress,在应用程序或运行正确进程上下文的驱动程序中使用 UserVirtualAddress。
按照下列步骤取消映射并释放缓冲区:
首先从用户地址空间中取消页面映射。应在映射 UserVirtualAddress 的进程上下文中运行时调用函数:
MmUnmapLockedPages(UserVirtualAddress, Mdl)
如果使用 MmProbleAndLockPages 锁定了页面,则使用以下函数解锁:
MmUnlockPages(Mdl)
释放 MDL:
IoFreeMdl(Mdl)
释放系统内存:
MmFreeContiguousMemory(SystemVirtualAddress)或
ExFreePool(SystemVirtualAddress)
共享内存对象方法
使用保存在页面文件上的内存映射文件是用于在用户进程之间共享内存的常用方法。不过,您可以使用以上技术在用户过程及设备驱动程序之间共享内存。使用这种技术有两种方法。在第一种方法中,驱动程序可以创建命名内存对象(称为 section 对象),一个或多个应用程序通过使用 OpenFileMapping 打开以上对象,然后调用 MapViewOfFile 函数来得到指向它的指针。通过指定以上对象的保护属性,可以定义某个进程使用内存的方式。
在第二种方法中,应用程序可以使用 CreateFileMapping 在用户模式下创建一个命名内存对象。驱动程序通过使用 ZwMapViewOfSection 函数及调用 ZwMapViewOfSection 得到其指针来打开以上内存对象。您应当使用异常处理程序在核心模式中访问该内存地址。有关使用这种技术的范例,请参见下列 Microsoft Knowledge Base 文章:
Q194945 样例:有关在核心和用户模式之间共享内存的 Section.exe
因为总是在进程的用户地址空间(小于 0x80000000,不管是在核心模式还是在用户模式中创建对象)上映射对象,所以只在进程上下文中访问地址时地址才有效。在相同内存对象上对 MapViewOfFile 或 ZwMapViewOfSection 的每次调用将返回一个不同的内存地址,即使对于相同的进程也是如此。通常不建议使用这种方法,特别是对于低级设备驱动程序而言。如前所述,这是因为地址范围被限制于映射对象的进程中,不能在 DPC 或 ISR 中访问。而且,在核心模式中创建内存对象的 API 没有记载于 DDK 中。
然而,要在提高的 IRQL (如 DPC 或 ISR)上使用该地址,您一定要查明并锁定缓冲区页面,并得到如 IOCTL 方法中所述的系统虚拟地址 (MmGetSystemAddressForMdl)。
只有准备在两个(或多个)用户进程和一个(或多个)设备驱动程序之间共享内存时,这种方法才比较简单容易。否则,使用 IOCTL 技术在一个用户过程及一个设备驱动程序之间共享内存将更为简单有效。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)