现在的位置: 首页 > 综合 > 正文

物理地址与虚拟地址

2013年10月29日 ⁄ 综合 ⁄ 共 4441字 ⁄ 字号 评论关闭

1.VirtualAlloc用来在进程的虚拟地址空间中保留(reserve)或者提交(commit)页。在保留时以64KB为粒度,即保留空间以64K为单位。而提交虚拟地址时,则以页(典型大小为4KB)为单位。

2.VirtualCopy用来绑定一块物理内存到当前进程虚拟地址空间。参数里的lpvSrc既可以是内核段的虚拟地址也可以是物理地址(用page_physical来标记)。同时要注意lpvSrc的右移与否。

3.使用VirtualAlloc要包含Winbase.h;使用VirtualCopy时要包含plfuncs.h.两者都要链接coredll.lib.

4.在CE5.0之前,使用VirtualAlloc获得的虚拟地址空间分为两种情形:
(1)大小在2MB以下时,位于调用进程的虚拟空间中;
(2)大小大于2MB时,位于用户态的共享地址空间内(0x42000000-0x7E000000 )

 

来源于:http://www.cnblogs.com/inspurhaitian/archive/2008/09/10/1288470.html

关于VirtualCopy函数中lpvSrc参数的设定,我有一个问题一直想不明白。
问题是,lpvSrc何时需要右移8位?

 

答案:1. 如果copy的物理地址在512M范围内,那么由于静态映射的存在,lpvSrc可以为静态映射的虚拟地址,也可以为物理地址。采用后者需要指定page_physical,同时lpvSrc右移8位。
2. 如果copy的物理地址在512M范围外,那么由于微软的如下规定“
VirtualCopy also supports the PAGE_PHYSICAL flag. You must set this
flag when you are mapping physical memory that resides beyond 512 MB,
that is, physical memory with an address above 0x1FFFFFFF.”
lpvSrc只能为物理地址,同时需要右移。

 

VirtualAlloc用来在进程的虚拟地址空间中保留(reserve)或者提交(commit)页。在保留时以64KB为粒度,即保留空间以64K为单位。而提交虚拟地址时,则以页(典型大小为4KB)为单位。

 

请问
VirtualCopy
这个函数是把寄存器的物理地址映射到VirtualAlloc申请的虚拟地址空间中?
还是已经在OEMaddressTable中把物理地址映射为虚拟地址1
再通过VirtualCopy把虚拟地址1映射到VirtualAlloc申请的虚拟地址中

 

如果是后者(在OEMaddressTable中把物理地址映射为虚拟地址1
再通过VirtualCopy把虚拟地址1映射到VirtualAlloc申请的虚拟地址中 )
那么为什么这么做呢?为什么不直接把物理地址映射到VirtualAlloc申请的虚拟地址中???

(个人认为不可能是后者,这样饶了几次)

 

VirtualAlloc 首先会从我们的虚拟地址空间中申请(或者说预留)一块虚拟空间,准备接下来要用它。注意此时,可用的物理内存并没有减少,只是虚拟地址少了一块可用的区域。
真正把这块之前reserved的虚拟空间映射到物理的内存区域就是由VirtualCopy来干的,此时,MMU的页表就会增加一个entry,来表示物理--虚拟的映射关系。

 

再说动态虚拟内存映射和静态虚拟内存映射
OEMaddressTable只是建立了一个一级的静态虚拟--物理的映射关系,一般给kernel(NK.EXE)通过直接访问的形式来用(OALPAtoVA);
MmMapIOSpace(VirtualAlloc +VirtualCopy)是一种动态虚拟映射的手段,一般给驱动根据当前需要(对硬件操作)动态申请并建立映射。

 

 

嵌入式设备与桌面PC的一个显著不同是它的应用程序中通常需要直接访问某一段物理内存,这在驱动程序中对物理内存的访问尤为重要,尤其是像ARM体系结构
下,I/O端口也被映射成某一个物理内存地址。因此,与桌面版本Windows相比,Windows
CE提供了相对简单的物理内存访问方式。无论是驱动程序还是应用程序都可以通过API访问某一段物理内存。
Windows CE的有些函数中需要用到物理内存结构体PHYSICAL_ADDRESS, Windows CE在ceddk.h中定义了PHYSICAL_ADDRESS,它其实是LARGE_INTEGER类型,其定义如下:
// in ceddk.h
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;
// in winnt.h
typedef union _LARGE_INTEGER{
struct{
DWORD LowPart;
LONG HighPart;
};
LONGLONG QuadPart;
} LARGE_INTEGER;
可见,Windows CE中用64个Bit来代表物理地址,对于大多数32位的CPU而言,只需要把它的HighPart设置为0就可以了。

 

 

如果要直接访问某一个地址的物理内存,Windows
CE提供了VirtualAlloc()和VirtualCopy()函数,VirtualAlloc负责在虚拟内存空间内保留一段虚拟内存,而
VirtualCopy负责把一段物理内存和虚拟内存绑定,这样,最终对物理内存的访问还是通过虚拟地址进行。它们的声明如下:
// 申请虚拟内存
LPVOID VirtualAlloc(
LPVOID lpAddress, // 希望的虚拟内存起始地址
DWORD dwSize, // 以字节为单位的大小
DWORD flAllocationType, // 申请类型,分为Reserve和Commit
DWORD flProtect // 访问权限
);
// 把物理内存绑定到虚拟地址空间
BOOL VirtualCopy(
LPVOID lpvDest, // 虚拟内存的目标地址
LPVOID lpvSrc, // 物理内存地址
DWORD cbSize, // 要绑定的大小
DWORD fdwProtect // 访问权限
);

 

VirtualAlloc对虚拟内存的申请分为两步,保留MEM_RESERVE和提交MEM_COMMIT。其中MEM_RESERVE只是在进程的虚
拟地址空间内保留一段,并不分配实际的物理内存,因此保留的虚拟内存并不能被应用程序直接使用。MEM_COMMIT阶段才真正的为虚拟内存分配物理内
存。
下面的代码显示了如何使用VirtualAlloc和VirtualCopy来访问物理内存。因为VirtualCopy负责把一段物理内存和虚拟内存绑定,所以VirtualAlloc的时候只需要对内存保留,没有必要提交。

FpDriverGlobals =
(PDRIVER_GLOBALS) VirtualAlloc(
       0,
       DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE,
       MEM_RESERVE,
        PAGE_NOACCESS);
if (FpDriverGlobals == NULL) {
      ERRORMSG(DRIVER_ERROR_MSG, (TEXT(" VirtualAlloc failed!/r/n")));
      return;
}
else {
      if (!VirtualCopy(
             (PVOID)FpDriverGlobals,
             (PVOID)(DRIVER_GLOBALS_PHYSICAL_MEMORY_START),
             DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE,
             (PAGE_READWRITE | PAGE_NOCACHE))) {
                                       ERRORMSG(DRIVER_ERROR_MSG, (TEXT("VirtualCopy failed!/r/n")));
            return;
     }
}

 

其实,MmMapIoSpace函数内部也是调用VirtualAlloc和VirtualCopy函数来实现物理地址到虚拟地址的映射的。
MmMapIoSpace函数的原代码是公开的,我们可以从%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/CEDDK
/DDK_MAP/ddk_map.c得到。从MmMapIoSpace的实现我们也可以看出VirtualAlloc和VirtualCopy的用法:
PVOID MmMapIoSpace (
IN PHYSICAL_ADDRESS PhysicalAddress,
IN ULONG NumberOfBytes,
IN BOOLEAN CacheEnable
)
{
PVOID pVirtualAddress; ULONGLONG SourcePhys;
ULONG SourceSize; BOOL bSuccess;

SourcePhys = PhysicalAddress.QuadPart & ~(PAGE_SIZE - 1);
SourceSize = NumberOfBytes + (PhysicalAddress.LowPart & (PAGE_SIZE - 1));

pVirtualAddress = VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS);
if (pVirtualAddress != NULL)
{
bSuccess = VirtualCopy(
pVirtualAddress, (PVOID)(SourcePhys >> 8), SourceSize,
PAGE_PHYSICAL | PAGE_READWRITE | (CacheEnable ? 0 : PAGE_NOCACHE));

if (bSuccess) {
(ULONG)pVirtualAddress += PhysicalAddress.LowPart & (PAGE_SIZE - 1);
}
else {
VirtualFree(pVirtualAddress, 0, MEM_RELEASE);
pVirtualAddress = NULL;
}
}
return pVirtualAddress;
}

外,Windows
CE还供了AllocPhysMem函数和FreePhysMem函数,用来申请和释放一段连续的物理内存。函数可以保证申请的物理内存是连续的,如果函
数成功,会返回虚拟内存的句柄和物理内存的起始地址。这对于DMA设备尤为有用。在这里就不详细介绍了,读者可以参考Windows CE的联机文档。

 

http://genewince.blogspot.com/2008/04/virtualcopy.html

 

 

抱歉!评论已关闭.