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 )
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只能为物理地址,同时需要右移
只要设置了PAGE_PHYSICAL 为真,那么就需要把lpvSrc右移8位
嵌入式设备与桌面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;
}
}
CEDDK还提供了函数MmMapIoSpace用来把一段物理内存直接映射到虚拟内存。此函数的原形如下:
PVOID MmMapIoSpace(
PHYSICAL_ADDRESS PhysicalAddress, // 起始物理地址
ULONG NumberOfBytes, // 要映射的字节数
BOOLEAN CacheEnable // 是否缓存
);
其实,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的联机文档。