?? 共享內(nèi)存.txt
字號(hào):
the easy way:
UserMode App : CreateFileMapping()
KernelMode : OpenSection()
我常用的方法是:(我的OS是NT)
1.應(yīng)用程序分配內(nèi)存空間,將其指針通過(guò)DeviceIoControl傳給驅(qū)動(dòng)程序
2.驅(qū)動(dòng)程序?qū)⒃撝羔樲D(zhuǎn)換為驅(qū)動(dòng)程序可用地址。首先,構(gòu)造一個(gè)mdl來(lái)描述該內(nèi)存(IoAllocateMdl);然后,調(diào)用MmProbeAndLockPages鎖定;最后,調(diào)用MmGetSystemAddressForMdl獲得系統(tǒng)地址。這樣就可以了。
記住,驅(qū)動(dòng)程序卸載時(shí)要釋放(MmUnLockPages, IoFreeMdl)
Hope it can help. : )
Buffered方式
當(dāng)I/O管理器創(chuàng)建IRP_MJ_READ或IRP_MJ_WRITE請(qǐng)求時(shí),它探測(cè)設(shè)備的緩沖標(biāo)志以決定如何描述新IRP中的數(shù)據(jù)緩沖區(qū)。如果DO_BUFFERED_IO標(biāo)志設(shè)置,I/O管理器將分配與用戶緩沖區(qū)大小相同的非分頁(yè)內(nèi)存。它把緩沖區(qū)的地址和長(zhǎng)度保存到兩個(gè)十分不同的地方,見(jiàn)下面代碼片段中用粗體字表示的語(yǔ)句。你可以假定I/O管理器執(zhí)行下面代碼(注意這并不是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);
可以看出,系統(tǒng)緩沖區(qū)地址被放在IRP的AssociatedIrp.SystemBuffer域中,而數(shù)據(jù)的長(zhǎng)度被放到stack->Parameters聯(lián)合中。在這個(gè)過(guò)程中還包含作為驅(qū)動(dòng)程序開(kāi)發(fā)者不必了解的額外細(xì)節(jié)。例如,讀操作之后的數(shù)據(jù)復(fù)制工作實(shí)際發(fā)生一個(gè)APC期間,在原始線程的上下文中,由一個(gè)與構(gòu)造該IRP完全不同的子例程執(zhí)行。I/O管理器把用戶模式虛擬地址(uva變量)保存到IRP的UserBuffer域中,這樣一來(lái)復(fù)制操作就可以找到這個(gè)地址。但你不要使代碼依賴(lài)這些事實(shí),因?yàn)樗鼈冇锌赡軙?huì)改變。IRP最終完成后,I/O管理器將釋放系統(tǒng)緩沖區(qū)所占用的內(nèi)存。
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系指向同一物理頁(yè)面,但訪問(wèn)權(quán)限不同),前者在2G以上且僅對(duì)系統(tǒng)可見(jiàn),后者則對(duì)用戶可讀寫(xiě)
我在內(nèi)核出分配一塊非分頁(yè)內(nèi)存,然后建立一個(gè)MDL來(lái)描述它,再將它映射到進(jìn)程空間,將映射得到的虛擬內(nèi)存指針通過(guò)IOCTL傳給應(yīng)用程序。然后我做兩個(gè)測(cè)試都失敗,請(qǐng)幫忙分析為什么:
測(cè)試1.在上述步驟成功后,在應(yīng)用程序?qū)戇@塊內(nèi)存,引起Access violacation錯(cuò)誤;
測(cè)試2.在驅(qū)動(dòng)程序?qū)懭腩A(yù)定數(shù)據(jù)到共享內(nèi)存,再到應(yīng)用程序查看這個(gè)地址,發(fā)現(xiàn)內(nèi)容沒(méi)有變化。
我的代碼如下:
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;
然后在應(yīng)用程序查看映射內(nèi)存地址,發(fā)現(xiàn)內(nèi)容沒(méi)有改變。
這篇文章是由微軟網(wǎng)站發(fā)表的,所以還是比較切實(shí)可用的。也可能有許多人早已精通了,那您就跳過(guò)此帖。
這篇文章中的信息適用于:
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
概要
本文討論在用戶模式和核心模式之間共享內(nèi)存的三種方法,使用何種方法取決于內(nèi)存緩沖區(qū)是由應(yīng)用程序分配的還是由設(shè)備驅(qū)動(dòng)程序分配的。
更多信息
IOCTL 方法:應(yīng)用程序分配共享內(nèi)存
在用戶模式與核心模式之間共享內(nèi)存的最簡(jiǎn)單有效的方法是使用 IOCTL。IOCTL 有四種不同的類(lèi)型,下列三種 IOCTL 使您可以在設(shè)備驅(qū)動(dòng)程序中直接訪問(wèn)用戶緩沖區(qū):
METHOD_IN_DIRECT
METHOD_OUT_DIRECT
METHOD_NEITHER
在以上這些方法中沒(méi)有創(chuàng)建中間系統(tǒng)緩沖區(qū)。
METHOD_IN_DIRECT 和 METHOD_OUT_DIRECT 自動(dòng)在 DeviceIoControl 中鎖定用戶指定的緩沖區(qū),并為驅(qū)動(dòng)程序創(chuàng)建內(nèi)存描述列表 (MDL)。驅(qū)動(dòng)程序可以從 MDL 得到一個(gè)系統(tǒng)地址,并根據(jù) IOCTL 傳輸類(lèi)型將信息傳入傳出緩沖區(qū)。由于所有的緩沖區(qū)驗(yàn)證、鎖定及 MDL 創(chuàng)建均是通過(guò) I/O 管理器來(lái)完成的,所以這是一種簡(jiǎn)單的方法。
相反,METHOD_NEITHER 直接將用戶緩沖區(qū)提供給驅(qū)動(dòng)程序,驅(qū)動(dòng)程序必須適當(dāng)?shù)仳?yàn)證緩沖區(qū)并鎖定緩沖區(qū),如果需要,還要為緩沖區(qū)獲取一個(gè)系統(tǒng)地址。雖然這是通過(guò)設(shè)備驅(qū)動(dòng)程序 I/O 子系統(tǒng)的最快捷路徑,但使用這種方法時(shí)也有一些應(yīng)當(dāng)注意的問(wèn)題。有關(guān)其它信息,請(qǐng)參見(jiàn) Microsoft Knowledge Base 中的下列文章:
Q126416 INFO:使用 METHOD_NEITHER IOCTL 的警告
要了解這三種不同的 IOCTL 如何工作,請(qǐng)參見(jiàn)以下 Microsoft Knowledge Base 文章:
Q178317 文件:IOCTL.exe:如何使用不同類(lèi)型的 IOCTL
MmMapLockedPages 方法:設(shè)備驅(qū)動(dòng)程序分配共享內(nèi)存
在這種方法中,驅(qū)動(dòng)程序通過(guò) MmAllocateContiguousMemory 或 ExAllocatePoolXxx 函數(shù)分配內(nèi)存,創(chuàng)建并建立描述緩沖區(qū)的 MDL,并使用 MmMapLockedPages 把內(nèi)存映射到用戶進(jìn)程地址空間中。用戶應(yīng)用程序可以使用由 MmMapLockPages 返回的虛擬地址直接訪問(wèn)系統(tǒng)內(nèi)存。
由于應(yīng)當(dāng)在訪問(wèn)內(nèi)存的進(jìn)程上下文中完成映射,所以此方法只能用于單層結(jié)構(gòu)的驅(qū)動(dòng)程序,這時(shí)可以保證 dispatch 例程在調(diào)用進(jìn)程上下文中運(yùn)行。您可以在任意數(shù)量的用戶過(guò)程地址空間上映射同一個(gè)系統(tǒng)緩沖區(qū)。不過(guò),應(yīng)當(dāng)設(shè)計(jì)一種保護(hù)機(jī)制,使驅(qū)動(dòng)程序及所有應(yīng)用程序?qū)?nèi)存的訪問(wèn)同步。進(jìn)一步說(shuō),應(yīng)當(dāng)在終止進(jìn)程之前或者一旦使用完緩沖區(qū)就在映射緩沖區(qū)的相同進(jìn)程上下文中取消緩沖區(qū)的映射。下面介紹將驅(qū)動(dòng)程序緩沖區(qū)映射至某個(gè)用戶進(jìn)程時(shí)需采取的步驟:
首先如下分配內(nèi)存:
SystemVirtualAddress = MmAllocateContiguousMemory(NumberOfBytes,
HighestAcceptableAddress); 或
SystemVirtualAddress = ExAllocatePool(PoolType, NumberOfBytes);
如下分配一個(gè) MDL:
Mdl = IoAllocateMdl(SystemVirtualAddress, NumberOfBytes, FALSE,
FALSE, NULL);
建立 MDL 以描述內(nèi)存頁(yè)。
MmBuildMdlForNonPagedPool(Mdl);
如下將鎖定的頁(yè)面映射至進(jìn)程的用戶地址空間中:
UserVirtualAddress = MmMapLockedPages(Mdl, UserMode);
前三步可以在 DriverEntry 或 Dispatch 例程中完成。但是,將緩沖區(qū)映射至用戶地址空間的最后一步,應(yīng)當(dāng)在運(yùn)行于調(diào)用進(jìn)程上下文的某個(gè)例程(通常是單層驅(qū)動(dòng)程序的 dispatch 例程)中完成。
您可以在某個(gè)提高的 IRQL 級(jí)及任何進(jìn)程上下文(即中斷服務(wù)例程 (ISR) 及延遲過(guò)程調(diào)用 (DPC))的驅(qū)動(dòng)程序中使用 SystemVirtualAddress,在應(yīng)用程序或運(yùn)行正確進(jìn)程上下文的驅(qū)動(dòng)程序中使用 UserVirtualAddress。
按照下列步驟取消映射并釋放緩沖區(qū):
首先從用戶地址空間中取消頁(yè)面映射。應(yīng)在映射 UserVirtualAddress 的進(jìn)程上下文中運(yùn)行時(shí)調(diào)用函數(shù):
MmUnmapLockedPages(UserVirtualAddress, Mdl);
如果使用 MmProbleAndLockPages 鎖定了頁(yè)面,則使用以下函數(shù)解鎖:
MmUnlockPages(Mdl)
釋放 MDL:
IoFreeMdl(Mdl);
釋放系統(tǒng)內(nèi)存:
MmFreeContiguousMemory(SystemVirtualAddress); 或
ExFreePool(SystemVirtualAddress);
共享內(nèi)存對(duì)象方法
使用保存在頁(yè)面文件上的內(nèi)存映射文件是用于在用戶進(jìn)程之間共享內(nèi)存的常用方法。不過(guò),您可以使用以上技術(shù)在用戶過(guò)程及設(shè)備驅(qū)動(dòng)程序之間共享內(nèi)存。使用這種技術(shù)有兩種方法。在第一種方法中,驅(qū)動(dòng)程序可以創(chuàng)建命名內(nèi)存對(duì)象(稱(chēng)為 section 對(duì)象),一個(gè)或多個(gè)應(yīng)用程序通過(guò)使用 OpenFileMapping 打開(kāi)以上對(duì)象,然后調(diào)用 MapViewOfFile 函數(shù)來(lái)得到指向它的指針。通過(guò)指定以上對(duì)象的保護(hù)屬性,可以定義某個(gè)進(jìn)程使用內(nèi)存的方式。
在第二種方法中,應(yīng)用程序可以使用 CreateFileMapping 在用戶模式下創(chuàng)建一個(gè)命名內(nèi)存對(duì)象。驅(qū)動(dòng)程序通過(guò)使用 ZwMapViewOfSection 函數(shù)及調(diào)用 ZwMapViewOfSection 得到其指針來(lái)打開(kāi)以上內(nèi)存對(duì)象。您應(yīng)當(dāng)使用異常處理程序在核心模式中訪問(wèn)該內(nèi)存地址。有關(guān)使用這種技術(shù)的范例,請(qǐng)參見(jiàn)下列 Microsoft Knowledge Base 文章:
Q194945 樣例:有關(guān)在核心和用戶模式之間共享內(nèi)存的 Section.exe
因?yàn)榭偸窃谶M(jìn)程的用戶地址空間(小于 0x80000000,不管是在核心模式還是在用戶模式中創(chuàng)建對(duì)象)上映射對(duì)象,所以只在進(jìn)程上下文中訪問(wèn)地址時(shí)地址才有效。在相同內(nèi)存對(duì)象上對(duì) MapViewOfFile 或 ZwMapViewOfSection 的每次調(diào)用將返回一個(gè)不同的內(nèi)存地址,即使對(duì)于相同的進(jìn)程也是如此。通常不建議使用這種方法,特別是對(duì)于低級(jí)設(shè)備驅(qū)動(dòng)程序而言。如前所述,這是因?yàn)榈刂贩秶幌拗朴谟成鋵?duì)象的進(jìn)程中,不能在 DPC 或 ISR 中訪問(wèn)。而且,在核心模式中創(chuàng)建內(nèi)存對(duì)象的 API 沒(méi)有記載于 DDK 中。
然而,要在提高的 IRQL (如 DPC 或 ISR)上使用該地址,您一定要查明并鎖定緩沖區(qū)頁(yè)面,并得到如 IOCTL 方法中所述的系統(tǒng)虛擬地址 (MmGetSystemAddressForMdl)。
只有準(zhǔn)備在兩個(gè)(或多個(gè))用戶進(jìn)程和一個(gè)(或多個(gè))設(shè)備驅(qū)動(dòng)程序之間共享內(nèi)存時(shí),這種方法才比較簡(jiǎn)單容易。否則,使用 IOCTL 技術(shù)在一個(gè)用戶過(guò)程及一個(gè)設(shè)備驅(qū)動(dòng)程序之間共享內(nèi)存將更為簡(jiǎn)單有效。
First Published: Aug 25 1998 4:30PM
關(guān)鍵字 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.在設(shè)備創(chuàng)建時(shí),分配一塊非分頁(yè)內(nèi)存;在設(shè)備Unload時(shí)刪除;中間你就可以訪問(wèn)這塊共享內(nèi)存,這里還有關(guān)鍵一步,將這塊非分頁(yè)內(nèi)存映射為應(yīng)用程序可以訪問(wèn)的內(nèi)存地址
可以參考以下的代碼,論壇中有很多類(lèi)似的例子
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為應(yīng)用程序可以訪問(wèn)的內(nèi)存地址
// FuncRetnVar 為IoControl的返回變量
}
WDM和應(yīng)用程序都可以從里面讀和寫(xiě)數(shù)據(jù)
2.因?yàn)槭欠欠猪?yè)內(nèi)存,盡量小(我是這樣認(rèn)為,還要為其他程序留出足夠的空間)
3.你可以試一下,結(jié)果告訴我一下
4.有區(qū)別,非分頁(yè)內(nèi)存位于真實(shí)物理內(nèi)存
5.應(yīng)該是這樣,速度變慢原因是在應(yīng)用程序配了盡可能多的內(nèi)存后,把你的部分內(nèi)存映射到windows的交換文件中。
//////////////////////////////////////////////////////////////////////////
SystemVirtualAddress = MmAllocateContiguousMemory(NumberOfBytes,
HighestAcceptableAddress);
或者:
SystemVirtualAddress = ExAllocatePool(PoolType, NumberOfBytes);
然后
Mdl = IoAllocateMdl(SystemVirtualAddress, NumberOfBytes, FALSE,
FALSE, NULL);
然后:
如果緩沖區(qū)是非分頁(yè)內(nèi)存
MmBuildMdlForNonPagedPool(Mdl);
如果十分頁(yè)內(nèi)存:
MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess);
ring3可以使用的virtual Address:
UserVirtualAddress = MmMapLockedPages(Mdl, UserMode);
然后應(yīng)用程序就可以使用這部分的緩沖區(qū)了.
給點(diǎn)分行嗎??別太摳了!! :) :)
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -