简单深入两个虚拟内存API VirtualAlloc及VritualCopy

  VirtualAlloc : (配置虛擬記憶體)

[MSDN] http://msdn.microsoft.com/en-us/library/aa366887(VS.85).aspx

LPVOID WINAPI VirtualAlloc (
   LPVOID lpAddress, //所要分配記憶體區域虛擬位址的起始位址
   DWORD dwSize, //要分配或者保留的區域的大小
   DWORD flAllocationType, //分配的類型:
   DWORD flProtect //指定所要分配區域的存取權限(保護方式)
    );

Windows CE上每一個Process其虛擬記憶體(virtual memroy)定址空間(address space)最多只有32 MB,這塊定址空間包含了程式碼﹑程式執行所需要的DLL﹑唯讀資料區域﹑堆疊空間(包含thread stack)外,剩餘的就由Heap所使用。C語言所使用的malloc含式就在Heap上配置記憶體供程式使用,當不在需要這塊記憶體時,可以呼叫 free含式歸還這塊記憶體。
由於最多只有32MB的定址空間,因此當使用malloc含式時,所能配置的記憶體總量是無法超過32MB的(還要扣掉程式碼等所使用的空間),那如果實際記憶體卻提供足夠的量,但是定址空間32MB不夠用時,該怎麼辦呢?要解決這個問題,我們必須更深入瞭解malloc含式。
malloc含式要配置記憶體時,要呼叫到系統所提供的VirtualAlloc含式,VirtualAlloc含式可以先保留一塊定址空間,當有需要時,再於這塊定址空間內配置實際記憶體,這塊記憶體的大小可以小於或等於保留的定址空間大小,當小於保留空間時,程式可以指定實際記憶體從這塊空間的那個位置開始配置。VirtualAlloc含式也可以在保留定址空間同時配置記憶體,如果呼叫成功,保留下來的定址空間可以馬上使用。基本上,malloc 含式以後者方式在保留定址空間時也配置了實際記憶體。
為了要讓程式可以使用超過32MB記憶體,Windows CE系統設定0x42000000~0x80000000為Large Memory Area(簡稱LMA),LMA主要提供定址空間給memory mapped file使用,但是程式透過VirtualAlloc含式也可以使用這塊定址區域。
當VirtualAlloc含式發現要求保留的size超過2MB大小時,會從LMA中找出一塊區域並保留,程式可以在這塊區域中再使用VirtualAlloc含式配置實際記憶體。(reference:http://www.wretch.cc/blog/sufan525/6899850)

flAllocationType詳細說明:

MEM_COMMIT,MEM_AUTO_COMMIT,MEM_RESERVE和MEM_TOP_DOWN。MEM_COMMIT標誌分配程式使用的記憶體,MEM_RESERVE保留虛擬位址空間以便以後提交。保留的頁不能存取直到調用VirtualAlloc的時候再次指定了MEM_COMMIT標誌。第三個標誌,MEM_TOP_DOWN,告訴系統從最高可允許的虛擬位址開始映射應用程式。

The MEM_AUTO_COMMIT標誌是唯一一個Windows CE最方便的標誌,當這個參數被指定了之後,區塊立即被保留,當其中的頁被第一次存取的時候,系統將自動提交該頁。這允許你分配大塊的虛擬記憶體而不需要顧及系統和實際RAM分配直到當前頁被第一次使用。自動提交記憶體的缺點是,物理RAM需要退回當頁面被第一次訪問時可能不可用的頁面。在這種情形下,系統將產生一個異常(exception)(可能會出現因為無法訪問而出錯)

MSDN:Reserves or commits a region of pages in the virtual address space of the calling process. Memory allocated by this function is automatically initialized to zero, unless MEM_RESET is specified.

To allocate memory in the address space of another process, use the VirtualAllocEx function.
VirtualAllocEx Function

LPVOID WINAPI VirtualAllocEx(
  __in      HANDLE hProcess,
  __in_opt  LPVOID lpAddress,
  __in      SIZE_T dwSize,
  __in      DWORD flAllocationType,
  __in      DWORD flProtect
);

Reserves or commits a region of memory within the virtual address space of a specified process. The function initializes the memory it allocates to zero, unless MEM_RESET is used.

如果想只配置 其他指定的Process的 記憶體空間,可以用VirtualAllocEX 函式,把實體控制碼放到第一個參數hProcess。

實作函式: DoVirtualAlloc

LPVOID DoVirtualAlloc( 
    LPVOID lpvAddress0,          
    DWORD cbSize,               
    DWORD fdwAllocationType,    
    DWORD fdwProtect,           
    DWORD fdwInternal,
    DWORD dwPFNBase
    )

(WINCEROOT/PRIVATE/WINCEOS/COREOS/NK/KERNEL/virtmem.c)

參數驗證:

1.cbSize 為 0
2.cbSize > 32 MB 且沒有指定虛擬記憶體起始位址
3.無效的存取控制資訊

    lpAddress:pointer指向指向要分配的region的起始位址。 如果分配的記憶體空間是reserved,那他的位址就會被rounded down 到下一個64-KB,如果記憶體空間不只是reserve,即將要commit,他的address就會被 rounded down到下一個 page (通常為1K或4K)。而如何決定一個一個page的大小則使用了 GetSystemInfo function(一個傳回現在系統資訊的function)。如果這個parameter值是NULL, 就由系統決定這個region要配置到哪裡。
    cbSize:以bytes為單位表示這個region的大小,如果lpAddress parameter是NULL,cbSize的值就會被rounded up to the next page boundary,否則,被配置的pages就會包括所有從lpAddress to lpAddress+cbSize的範圍。這表示了一個2-byte range跨越了page boundary,造成了有兩個page被包含在被配置的region。
fdwAllocationType:表示配置的型態。以下的表列出了這些型態:

Value

Description

MEM_COMMIT  

以Page為單位

specified region of pages配置在memory或配置在diskpaging file 。試圖commit一個已經被commitpage將不會造成這個functionfail 這表示不管是已經被commit或還沒被commitpages是否會造成functionfailure

MEM_RESERVE 

以Page為單位,但有64KB granularity boundary的限制

Reserves 一段virtual address spaceprocess但是不配給他任何實體儲存空間。這一段被 reserve的範圍不可以再被其他allocation operations使用。Reserved pages 可以在接下來呼叫的DoVirtualAlloc時被committe

MEM_RESET   (以page為單位)

WinCE不支援

MEM_TOP_DOWN

配置highest possible addressmemory .

其中MEM_RESERVE只是在Process的虛擬地址空間內保留一段虛擬記憶體,並不配置實體記憶體,因此保留的虛擬記憶體並不能被應用程式直接使用。MEM_COMMIT階段才真正的為虛擬記憶體配置實體記憶體。

 

  fdwProtect:表示存取的保護型態。.如果 pages正在被committee,底下列出所有可能發生的型態:
參考MSDN : http://msdn.microsoft.com/en-us/library/aa366786(VS.85).aspx

Value

Description

PAGE_READONLY

對於被commited pages有存取的權力, 如果試圖對commited region 進行寫入會造成access violation 

該區域為唯讀。如果應用程式試圖訪問區域中的頁的時候,將會被拒絕訪問。

PAGE_READWRITE

對於 commited pages有讀和寫的權力
區域可被應用程式讀寫。

PAGE_EXECUTE

對於commited pages有執行的權力,任何對commited pages有讀或寫的動作都會造成access violation
區域包含可被系統執行的代碼。試圖讀寫該區域的操作將被拒絕。

PAGE_EXECUTE_READ

允許在commited pages有執行和讀取的權力,任何試圖對commited pages進行寫入動作都會被視作access violation
區域包含可執行代碼,應用程式可以讀取該區域。

PAGE_EXECUTE_READWRITE

允許對commited pages 進行執行讀取以及寫入的動作。
區域包含可執行代碼,應用程式可以讀寫該區域。

PAGE_GUARD

在這一個region裡的page都會視guard pages.,任何試圖讀或寫guard page都會造成系統raise一個STATUS_GUARD_PAGE exception,如果一個guard page exceptionsystem service時發生servicereturn一個失敗的 indicator
區域第一次被訪問時進入一個STATUS_GUARD_PAGE異常,這個標誌要和其他保護標誌合併使用,表明區域被第一次訪問的許可權。

PAGE_NOACCESS

不允許任何對commited pages 的存取,任何試圖去讀或寫,執行commited pages都會造成access violation exception,(稱為a general protection (GP) fault.
任何存取該區域的操作將被拒絕

PAGE_NOCACHE

不允許committed regions of pages擁有cache,由hardware來支援。在一般的情況下並不適用,對於device drivers比較有用。

對應至此區域的RAM分頁將無法由處理器快取。

PAGE_GUARD和PAGE_NOCHACHE標誌可以和其他標誌合併使用以進一步指定頁的特徵。PAGE_GUARD標誌指定了一個防護頁(guard page),即當一個頁被提交時會因第一次被訪問而產生一個one-shot異常,接著取得指定的存取權限。PAGE_NOCACHE防止當它映射到虛擬頁的時候被微處理器快取緩衝。這個標誌方便設備驅動使用直接記憶體存取方式(DMA)來共用記憶體區塊的裝置驅動程式(Device Driver)而言是很便利的。

 

範例1:

虛擬記憶體在區域內被保留是以64KB為page boundary的。(註:參考WindowsCE 嵌入式系統 理論與實務ISBN:986-125-200-2與此文章,因為Memblock的一個單位就是64K,64K是因為以前16位元的舊系統定義並保留下來的,而依據系統不同而會有不同的分頁大小,通常是1K或4K,以上是本人亂猜的,可參考http://blogs.msdn.com/oldnewthing/archive/2003/10/08/55239.aspx )

因為對每個Process有32MB虛擬記憶體位址空間的限制,這就有了一個最大值 32MB/64KB-1(Guard Section Reserved)=511,這是虛擬記憶體在記憶體溢出前能被保留的最大值。

#define PAGESIZE 1024 // Assume we're on a 1-KB page machine
  for (i = 0; i < 512; i++)
  pMem[i] = VirtualAlloc (NULL, PAGESIZE, MEM_RESERVE │ MEM_COMMIT,PAGE_READWRITE);

該程式碼配置了512個單頁的虛擬記憶體。甚至你系統還有一半的可用RAM,VirtualAlloc也會在完成配置前失敗。因為它的運作已經超出了應用程式的虛擬位址空間。發生這種情況是因為每1-KB的區塊要佔用64-KB的空間
(註:因為保留最小以64KB為單位,假如只有1KB在實體記憶體,64KB的虛擬記憶體也會被全部Reserved到,也就是每執行一次迴圈,VirtualAlloc就幫你保留64k而不是1k)
,接下來應用程式的Code,Stack,和Local Heap也要映射到同樣的32-MB虛擬位址空間,可用的虛擬分配區域通常不超過475個。( 已經用了2304KB=2.25MB )

 

一個比較好的分配512k(此例也等於512頁)虛擬記憶體的方法是這樣做:

#define PAGESIZE 1024 // Assume we're on a 1-KB page machine.
  // Reserve a region first.
  pMemBase = VirtualAlloc (NULL, PAGESIZE * 512, MEM_RESERVE,PAGE_NOACCESS);
  for (i = 0; i < 512; i++)
  pMem[i] = VirtualAlloc (pMemBase + (i*PAGESIZE), PAGESIZE,MEM_COMMIT, PAGE_READWRITE);
 

程式碼首先保留了Reserved一塊區域,頁面將在以後被提交Commit。因為區域已經被先保留了,且提交頁Commit Page不受64-KB granularity boundary的規定,如果你系統中有512KB的可用實體記憶體,分配將會成功。(這樣實體記憶體不會因為保留而浪費了空間)

  儘管我剛才給你看的是一個人為的例子(還有比直接分配虛擬記憶體更好的方法來分配1-KB的區塊),這中記憶體分配方法驗證了一個重要的不同(對於其他Windows系統)。在桌上出版本的Windows中,工作中的應用程式有一個完全的2-GB的虛擬位址空間。在Windows CE中,一個程式師必須明白每個應用程式只被保留了較小的32-MB虛擬位址空間。

 

 

範例2:

配置64MB虛擬記憶體

VirtualAlloc含式也可以在保留定址空間同時配置記憶體,如果呼叫成功,保留下來的定址空間可以馬上使用。基本上,malloc 含式以這方式在保留定址空間時也配置了實際記憶體。這作法並不適合保留定址空間同時配置記憶體,因此如果程式呼叫
VirtualAlloc(0, 1024 * 1024 * 4, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);  
時所配置4MB的記憶體還是在process所擁有的32MB區域內
,要用到 LMA區域的正確作法是
pVirStart = VirtualAlloc(0, 1024 * 1024 * 64, MEM_RESERVE, PAGE_NOACCESS);
ptrMem[0] = VirtualAlloc((PVOID)(pVirStart, 1024 * 1024 * 16, MEM_COMMIT, PAGE_READWRITE);
ptrMem[1] = VirtualAlloc((PVOID)(ptrMem[0] + (1024 * 1024 * 16), 1024 * 1024 * 16, MEM_COMMIT, PAGE_READWRITE);
ptrMem[2] = VirtualAlloc((PVOID)(ptrMem[1] + (1024 * 1024 * 16), 1024 * 1024 * 16, MEM_COMMIT, PAGE_READWRITE);
ptrMem[3] = VirtualAlloc((PVOID)(ptrMem[2] + (1024 * 1024 * 16), 1024 * 1024 * 16, MEM_COMMIT, PAGE_READWRITE);

這樣就可以配置4塊16MB的區域(總共64MB)供程式使用。要注意的是,在配置實際記憶體時要求的大小也有限制,在這個例子中,如果要求一次配置64MB或32MB實際記憶體會失敗(即使定址空間足夠),至於系統一次最大可以要求配置多少實際記憶體,這點還待探索。

VirtualAlloc(0, 1024 * 1024 * 64, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); //配置失敗

除了瞭解如何讓配置記憶體超過32MB區域外,VirtualAlloc含式還大有學問,如果你不瞭解清楚就貿然使用,可能會出現一些出乎意料的結果。比如說,我們都清楚記憶體是以page為單位,一個page是4K,因此記憶體最小配置不能小於4K。但4K真的是最小的配置量嗎?如果你配置8K記憶體,系統是否就配置2個page的空間?答案是否定的,系統一次配置記憶體的最小量可以從GetSystemInfo含式取得,GetSystemInfo含式需要傳入一個SYSTEM_INFO變數,這個變數中的dwAllocationGranularity欄位就是系統一次配置的最小量,也就是說,如果 dwAllocationGranularity為64KB,而你要求配置16K記憶體,系統實際上會配置64K記憶體,如果實際有128K記憶體,那你可能會認為只配置32K就沒有記憶體了,實際上是所有記憶體都配置給你囉。。(reference:http://www.wretch.cc/blog/sufan525/6899850)

 

VirtualCopy: (實體位址動態對應至虛擬位址)

[MSDN] http://msdn2.microsoft.com/en-us/library/aa908789.aspx

BOOL VirtualCopy( 
    LPVOID lpvDest, // 要對應至目標的虛擬位址,這個位址必須已保留Reserved
    LPVOID lpvSrc, // 對應來源的位址,這個位址必須已提交Commit(可以使用的一段實體位址或記憶體)
    DWORD cbSize, // 要對應區域的大小,以位元組為單位
    DWORD fdwProtect // 設定存取權限
    );

MSDN:This function dynamically maps a virtual address to a physical address by creating a new page-table entry. Terminate the mapping by calling VirtualFree.

PAGE_PHYSICAL Used to map a physical memory region. When using this flag, divide the physical address, that is, lpvSrc, by 256. Memory mapped with PAGE_PHYSICAL is not freed until the device is rebooted. Calling the VirtualFree function does not free this mapped physical memory. This flag is used with dedicated hardware buffers, so it cannot be freed after being mapped.

雖然以位元組為單位,但是VirtualCopy的操作做小單位是Page.

Remarks WinCE6.0

In CE 6.0 and later, VirtualCopy is a kernel-mode-only function. For information about other kernel-mode only functions, see Kernel Mode APIs.

For CE 6.0, a call to VirtualCopy fails if it crosses a 32-MB section boundary. For example, if you allocate 16 MB and it crosses a 32 MB boundary, you must make two VirtualCopy calls.

WinCE6還有VirtualCopyEX可以使用.

Calling VirtualCopy from user mode work only if it is called from a user-mode driver, where its physical address range is specified in the registry. To port code that calls VirtualAlloc or VirtualCopy, consider doing one of the following, which are in order of preference in terms of security and robustness:

  • Make the code that calls VirtualAlloc or VirtualCopy a driver.
  • Make the code that calls VirtualAlloc or VirtualCopy into a DLL, and use LoadKernelLibrary to load it into kernel.
  • Create a dummy kernel mode driver that implements IOCTL_DO_VIRTUAL_COPY to call VirtualCopy.

Windows CE在ceddk.h中定義了PHYSICAL_ADDRESS,它其實是LARGE_INTEGER類型,其定義如下:
// in ceddk.h
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;
範圍:--3689348814741910324到+4611686018427387903  

// in winnt.h 
typedef union _LARGE_INTEGER{ 
   struct{ 
    DWORD LowPart; //4byte=32bit 
    LONG HighPart; //4byte=32bit 
   }; 
    LONGLONG QuadPart; //8byte=64bit 
   } LARGE_INTEGER; 

56000000 = 是這樣存的
  LowPart        HighPart 
00 00 00 56  00 00 00 00

QuadPart=0x0000000056000000

可見,Windows CE中用64個Bit來代表實體位址,對於大多數32位的CPU而言,只需要把它的HighPart設置為0就可以了。

範例1:

The following code example shows how VirtualCopy is called to map a 128-KB region at physical address 0x64000000.

VirtualCopy(pvDest, (void *) (0x64000000/256), 128*1024, PAGE_READWRITE | PAGE_PHYSICAL | PAGE_NOCACHE);

 

範例2:

pVMem = (PUCHAR)VirtualAlloc(0, PAGE_SIZE, MEM_RESERVE, PAGE_NOACCESS); 
if (pVMem) { 
// map in GPIO registers 
bMapReturn = VirtualCopy( pVMem, (LPVOID)(S3C2443_BASE_REG_PA_IOPORT>>8), PAGE_SIZE, PAGE_READWRITE | AGE_NOCACHE |PAGE_PHYSICAL); 
pLED->pIOPReg = (volatile S3C2443_IOPORT_REG*)(pVMem);

1.S3C2443_BASE_REG_PA_IOPORT使用的實體物理位址,要在第四個參數加上PAGE_PHYSICAL,而且在第二個參數的來源位址要除256(右移8),但是在實作函式DoVirtualCopy裡會在(左移8)移回來

2.VirtualCopy負責把一段實體位址和虛擬位址做對應,所以VirtualAlloc的時候只需要對虛擬記憶體保留,沒有必要提交來實際分配實體記憶體。

3.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設備非常有用。

 

[MSDN]How To Configure Windows CE for Use With DMA Devices:
http://support.microsoft.com/kb/299355/en-us/

因為WinCE沒有直接支援DMA,在沒有MapVirtualToPhysical API 下,要如何把一段保留的位址對應至32MB的應用程式處理空間(Process),讓ISR可以直接存取它,我們可以使用VirtualAlloc和VirtualCopy函式,首先我們會保留一段實體位址給DMA裝置,然後透過以步驟,即可把實體位址對應至User Mode Process或是Driver Address Spaces.

範例3:

1.In the "MEMORY" section of the Config.bib file, add a "RESERVED" section, as in the following example:

;name  hex start address  hex size in bytes   keyword RESERVED
MY_DMA 8003000 00004000 RESERVED

我們先必須在Config.bib設定保留一段以後可以DMA的地址,DMA裝置,這個位址是實體位址。

This will reserve a 16-KB contiguous block of memory starting at address 8003000. By definition, any memory reserved with the RESERVED keyword is contiguous.
ISR and kernel mode processes can then directly access this memory, but user mode drivers and applications must use the VirtualAlloc and VirtualCopy commands to map the memory into their address space.
NOTE: From within your ISR, which runs in kernel mode, you can directly reference this memory.

2.From a user mode application or driver you must use code similar to the following sample:

pMem = VirtualAlloc(pStart, dwSize, MEM_RESERVE, PAGE_READWRITE);
// pStart is the start of the virtual address range desired.
// dwSize is the size in bytes of the region to access.
// MEM_RESERVE indicates that the OS should reserve the address space but not back it with physical memory.
// pMem will contain the pointer to the virtual memory block if the call is successful or NULL if it fails.<BR/>
// now map the RESERVED memory to this address space:
LPVOID pSrc = 8003000; // assign source to our RESERVED memory
DWORD dwSize = 0x4000; // size of memory; from example bib entry above
BOOL bRetCode; 
bRetCode = VirtualCopy(pStart,pSrc/256, dwSize, PAGE_READWRITE | PAGE PHYSICAL,);

// PAGE_PHYSICAL is necessary if pSrc is physical memory with a starting address above 0x1FFFFFFF 
// When using PAGE_PHYSICAL it is also necessary to divide the source address by 256. 
// bRetCode is TRUE for success, FALSE for failure.

 

 

 

VirtualFree: (取消提交Decommit或釋放Release虛擬記憶體)

[MSDN] http://msdn.microsoft.com/en-us/library/aa366892(VS.85).aspx

BOOL VirtualFree(
    LPVOID lpAddress, // 指向要被釋放或取消提交的虛擬記憶體的區域的起始位址
    DWORD dwSize, //取消提交區域的大小,以位元組為單位。如果區域要被Release,這個值必須是0
    DWORD dwFreeType //MEM_DECOMMIT標誌指定了區域將被取消提交但是仍被保留,
                    //MEM_RELEASE標誌說明區域要取消提交並且釋放。
    );

MSDN:Releases, decommits, or releases and decommits a region of pages within the virtual address space of the calling process.To free memory allocated in another process by the VirtualAllocEx function, use the VirtualFreeEx function.

在區域中的所有的頁通過VirtualFree被釋放必須處在同樣的情況下。更確切地說,區域中的全部頁要被釋放,那這些頁要麼都是被提交的頁,要麼都是被保留的頁。如果有些頁被提交,有些頁被保留,那麼VirtualFree函式呼叫就會失敗。所以要先把提交的先DeCommit 再來一起 Release。( = MEM_DECOMMIT 與 MEM_RELEASE 不能同時使用)

範例:

VirtualFree((PVOID)pLED->pIOPReg, 0, MEM_RELEASE);

 

VirtualProtect: (變更虛擬記憶體存取權限)
[MSDN]: http://msdn.microsoft.com/en-us/library/aa366898(VS.85).aspx

BOOL VirtualProtect(
    LPVOID lpAddress, // 指向要被修改虛擬記憶體的區域的起始位址 
    DWORD dwSize, // 大小
    DWORD flNewProtect, //設定新的保護標誌。與VirtualAlloc相同。
    PDWORD lpflOldProtect // 原先虛擬記憶體第一個分頁的保護標誌。設置NULL或錯誤該函式會失敗。
    );

 

 

VirtualQuery: (查詢虛擬記憶體存取權限)

[MSDN]: http://msdn.microsoft.com/en-us/library/aa366902(VS.85).aspx

DWORD VirtualQuery(
    LPCVOID lpAddress, // 開始查詢的位址
    PMEMORY_BASIC_INFORMATION lPBuffer, //
    DWORD dwLength, //PMEMORY_BASIC_INFORMATION結構的大小。
    );

 

PMEMORY_BASIC_INFORMATION結構被定義如下: 

 typedef struct _MEMORY_BASIC_INFORMATION {
  PVOID BaseAddress; //BaseAddress,是傳遞給VirtualQuery函數的一個位址。
  PVOID AllocationBase;//使用VirtualAlloc函數分配的區域的基底位址
  DWORD AllocationProtect;//原來,一開始被VirtualAlloc分配時的保護屬性
  DWORD RegionSize;//傳遞給VirtualQuery的指標開始到一系列具有相同屬性的頁為結尾的區域大小
  DWORD State;//區域中頁的狀態-自由,保留,提交。
  DWORD Protect;//MEM_PRIVATE,MEM_MAPPED,MEM_IMAGE
  DWORD Type;//記憶體的類型
  } MEMORY_BASIC_INFORMATION;

//Protect欄位可以包含MEM_PRIVATE標誌,指明該區域包含應用程式私有的資料;
//MEM_MAPPED指明該區域被映射為一個記憶體映射檔;
//MEM_IMAGE指明該區域被映射為一個EXE或DLL模組。

範例參考 WindowsCE.NET 程式設計3(Microsoft Press) 7-14頁
 

 

 

其他資料紀錄:

E:/WINCE500/PRIVATE/WINCEOS/COREOS/NK/KERNEL/virtmem.c 
DoVirtualCopy() 
 
E:/WINCE500/PRIVATE/WINCEOS/COREOS/NK/KERNEL/virtmem.c 
#define IsKernelVa(va)      (((DWORD)(va) & 0x80000000) && !IsSecureVa(va)) 
#define PFNfrom256(pg256)   ((ulong)(pg256)<<8 & ~(PAGE_SIZE-1)) 
#define GetPFN(pgpi)  ((FirstPT[(ulong)(pgpi)>>20]&0xFFF00000) | ((ulong)(pgpi)&0x000FF000)) 
 
E:/WINCE500/PRIVATE/WINCEOS/COREOS/NK/INC/mem_arm.h 
#if (defined(ARMV4) || defined(ARMV4T) || defined(ARMV4I))     // uses 4K page tables 
#define VA_PAGE         12 
#define L2_MASK         0xFF    // For a 4K page size (small pages) 
#define PAGE_SIZE       4096 
#elif defined(ARM920)   // uses 1K page tables 
#define VA_PAGE         10 
#define L2_MASK         0x3FF 
#define PAGE_SIZE       1024 
#endif 
 
E:/WINCE500/PRIVATE/WINCEOS/COREOS/NK/INC/nkarm.h 
#define ArmHigh ((ARM_HIGH *)0xFFFD0000) 
#define FirstPT (ArmHigh->firstPT) 
 
typedef struct ARM_HIGH { 
    ulong   firstPT[4096];      // 0xFFFD0000: 1st level page table 
    char    reserved2[0x20000-0x4000]; 
 
    char    exVectors[0x400];   // 0xFFFF0000: exception vectors 
    char    reserved3[0x2400-0x400]; 
 
    char    intrStack[0x400];   // 0xFFFF2400: interrupt stack 
    char    reserved4[0x4900-0x2800]; 
 
    char    abortStack[0x700];  // 0xFFFF4900: abort stack 
    char    reserved5[0x6800-0x5000]; 
 
    char    fiqStack[0x100];    // 0xFFFF6800: FIQ stack 
    char    reserved6[0xC000-0x6900]; 
 
    char    kStack[0x800];      // 0xFFFFC000: kernel stack 
    struct KDataStruct kdata;   // 0xFFFFC800: kernel data page 
} ARM_HIGH; 
 

----------------------------------------------------------------------------------------------------------------

CE6下還有很多API是AP和user模式的驅動不能使用的,

大家要把CE50下的AP移植到6.0下一定要注意找到替代方案
Virtual Memory APIs
CeVirtualSharedAlloc
LockPages
LockPagesEx
UnlockPages
UnlockPagesEx
VirtualAllocCopyEx
VirtualCopyEx
VirtualSetAttributes
CreateStaticMapping
NKDeleteStaticMapping
VirtualCopy
File System APIs
ReadRegistryFromOEM
SetStoreQueueBase
WriteRegistryToOEM
Power APIs
PowerOffSystem (很多測試AP用到)
Miscellaneous APIs
SetOOMEvent

你可能感兴趣的:(windows,exception,api,function,Integer,WinCE)