⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 共享内存.txt

📁 驱动开发过程中要注意的一些要点以及一些基本资料
💻 TXT
字号:
the easy way:
UserMode App : CreateFileMapping()
KernelMode : OpenSection()
我常用的方法是:(我的OS是NT)
1.应用程序分配内存空间,将其指针通过DeviceIoControl传给驱动程序
2.驱动程序将该指针转换为驱动程序可用地址。首先,构造一个mdl来描述该内存(IoAllocateMdl);然后,调用MmProbeAndLockPages锁定;最后,调用MmGetSystemAddressForMdl获得系统地址。这样就可以了。

记住,驱动程序卸载时要释放(MmUnLockPages, IoFreeMdl)

Hope it can help. : )


 Buffered方式
当I/O管理器创建IRP_MJ_READ或IRP_MJ_WRITE请求时,它探测设备的缓冲标志以决定如何描述新IRP中的数据缓冲区。如果DO_BUFFERED_IO标志设置,I/O管理器将分配与用户缓冲区大小相同的非分页内存。它把缓冲区的地址和长度保存到两个十分不同的地方,见下面代码片段中用粗体字表示的语句。你可以假定I/O管理器执行下面代码(注意这并不是Windows NT的源代码):

PVOID uva; // user-mode virtual buffer address
ULONG length; // length of user-mode buffer

PVOID sva = ExAllocatePoolWithQuota(NonPagedPoolCacheAligned, length);
if (writing)
RtlCopyMemory(sva, uva, length);

Irp->AssociatedIrp.SystemBuffer = sva; 

PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp);
if (reading)
stack->Parameters.Read.Length = length; 
else
stack->Parameters.Write.Length = length; 

<code to send and await IRP>

if (reading)
RtlCopyMemory(uva, sva, length);

ExFreePool(sva);


可以看出,系统缓冲区地址被放在IRP的AssociatedIrp.SystemBuffer域中,而数据的长度被放到stack->Parameters联合中。在这个过程中还包含作为驱动程序开发者不必了解的额外细节。例如,读操作之后的数据复制工作实际发生一个APC期间,在原始线程的上下文中,由一个与构造该IRP完全不同的子例程执行。I/O管理器把用户模式虚拟地址(uva变量)保存到IRP的UserBuffer域中,这样一来复制操作就可以找到这个地址。但你不要使代码依赖这些事实,因为它们有可能会改变。IRP最终完成后,I/O管理器将释放系统缓冲区所占用的内存。

SysBuffer = ExAllocatePoolWithTag (NonPagedPool,MAXMODNUM*sizeof(*SysBuffer),0x206B6444); 
if(!SysBuffer) 
KdPrint( ("HookTdi: Allocate SysBuffer fail!n") ); 
else 
{ 
//Map into Userspace 
pMDL = ExAllocatePoolWithTag(NonPagedPool, 
MmSizeOfMdl(SysBuffer,MAXMODNUM*sizeof(*SysBuffer)), 
0x206B6444); 
if(!pMDL) 
KdPrint( ("HookTdi: No enough Mem for MDL!n") ); 
else 
{ 
pMDL = MmCreateMdl (pMDL,SysBuffer,MAXMODNUM*sizeof(*SysBuffer)); 
MmBuildMdlForNonPagedPool(pMDL); 
UserBuffer = MmMapLockedPages(pMDL,1); 
} 

} 
地方太窄了,贴不下代码!各位凑活看吧。将SysBuffer映射到用户空间(SysBuffer&UserBuffer系指向同一物理页面,但访问权限不同),前者在2G以上且仅对系统可见,后者则对用户可读写
我在内核出分配一块非分页内存,然后建立一个MDL来描述它,再将它映射到进程空间,将映射得到的虚拟内存指针通过IOCTL传给应用程序。然后我做两个测试都失败,请帮忙分析为什么:
测试1.在上述步骤成功后,在应用程序写这块内存,引起Access violacation错误;
测试2.在驱动程序写入预定数据到共享内存,再到应用程序查看这个地址,发现内容没有变化。
我的代码如下:

PVOID pSharedBuffer = NULL;
PMDL pMdl = NULL;
PVOID pToUser = NULL;

pSharedBuffer = ExAllocatePool(NonPagedPool,
64*400);
if(!pSharedBuffer)
{
return STATUS_INSUFFICIENT_RESOURCE;
}
pMdl = IoAllocateMdl(pSharedBuffer,64*400,FALSE,FALSE,NULL);
if(!pMdl)
{
ExFreePool(pSharedBuffer);
return STATUS_INSUFFICIENT_RESOURCE;
}

MmBuildMdlForNonPagedPool( pMdl );

pToUser = MmMapLockedPages( pMdl,UserMode );

if( !pToUser )
{
MmUnMapLockedPages(pToUser,pMdl);
IoFreeMdl( pMdl );
ExFreePool( pSharedBuffer );
return STATUS_INSUFFICIENT_RESOURCE;
}

RtlZeroMemory(lpSharedBuffer,64*400); 
char TestString[]="This is a test";
RtlCopyMemory(lpSharedBuffer,TestString,sizeof(TestString));

return STATUS_SUCCESS;

然后在应用程序查看映射内存地址,发现内容没有改变。
这篇文章是由微软网站发表的,所以还是比较切实可用的。也可能有许多人早已精通了,那您就跳过此帖。

这篇文章中的信息适用于:
Microsoft Win32 Device Driver Kit (DDK) for Windows NT 3.1, 3.5, 3.51, 4.0
Microsoft Windows 2000 Advanced Server
Microsoft Windows 2000 Server
Microsoft Windows 2000 Professional

概要
本文讨论在用户模式和核心模式之间共享内存的三种方法,使用何种方法取决于内存缓冲区是由应用程序分配的还是由设备驱动程序分配的。 
更多信息
IOCTL 方法:应用程序分配共享内存 


在用户模式与核心模式之间共享内存的最简单有效的方法是使用 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);
如下将锁定的页面映射至进程的用户地址空间中: 
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 技术在一个用户过程及一个设备驱动程序之间共享内存将更为简单有效。

First Published: Aug 25 1998 4:30PM 
关键字 kbDDK kbKMode kbOSWinNT400 kbOSWin2000 _IK kbhowto 



 

____________________

Fight, and you may die;
Run, and you’ll live,
At least awhile, 
And dying in your beds, many years from now,
Would you be willing to trade all the days from this day to that for one chance, just one chance,
To come back here and tell our enemies that they may take our lives but they’ll never take our freedom!


1.在设备创建时,分配一块非分页内存;在设备Unload时删除;中间你就可以访问这块共享内存,这里还有关键一步,将这块非分页内存映射为应用程序可以访问的内存地址
可以参考以下的代码,论坛中有很多类似的例子
pMdl = (PMDL)ExAllocatePoolWithTag(NonPagedPool, SizeOfMdl(IoSysBuffer,ShareBufferLen),0x206B6444); //Tag=Ddk
if(pMdl)
{
pMdl = MmCreateMdl(pMdl,IoSysBuffer,ShareBufferLen); 
MmBuildMdlForNonPagedPool(pMdl); 
IoUserBuffer = MmMapLockedPages(pMdl, UserMode); // 映射
FuncRetnVar = (ULONG)IoUserBuffer;
// IoUserBuffer为应用程序可以访问的内存地址
// FuncRetnVar 为IoControl的返回变量
}
WDM和应用程序都可以从里面读和写数据
2.因为是非分页内存,尽量小(我是这样认为,还要为其他程序留出足够的空间)
3.你可以试一下,结果告诉我一下
4.有区别,非分页内存位于真实物理内存
5.应该是这样,速度变慢原因是在应用程序配了尽可能多的内存后,把你的部分内存映射到windows的交换文件中。
//////////////////////////////////////////////////////////////////////////
SystemVirtualAddress = MmAllocateContiguousMemory(NumberOfBytes, 
HighestAcceptableAddress); 
或者: 
SystemVirtualAddress = ExAllocatePool(PoolType, NumberOfBytes); 
然后 
Mdl = IoAllocateMdl(SystemVirtualAddress, NumberOfBytes, FALSE, 
FALSE, NULL); 
然后: 
如果缓冲区是非分页内存 
MmBuildMdlForNonPagedPool(Mdl); 
如果十分页内存: 
MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess); 

ring3可以使用的virtual Address: 
UserVirtualAddress = MmMapLockedPages(Mdl, UserMode); 

然后应用程序就可以使用这部分的缓冲区了. 

给点分行吗??别太抠了!! :) :)


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -