Windows CE下訪問實體記憶體的方法
轉載來自http://www.cppblog.com/milkyway/articles/18269.html
嵌入式裝置與案頭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用來把一段實體記憶體直接映射到虛擬記憶體。用MmMapIoSpace申請的記憶體要用MmUnmapIoSpace釋放,此函數的原形如下:
PVOID MmMapIoSpace(
PHYSICAL_ADDRESS PhysicalAddress, // 起始物理地址
ULONG NumberOfBytes, // 要映射的位元組數
BOOLEAN CacheEnable // 是否緩衝
);
VOID MmUnmapIoSpace(
PVOID BaseAddress, // MmMapIoSpace返回的起始虛擬位址
ULONG NumberOfBytes //
);
其實,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的聯機文檔。