Prenotazione e commit della memoria
Nell'esempio seguente viene illustrato l'uso delle funzioni VirtualAlloc e VirtualFree per riservare e eseguire il commit della memoria in base alle esigenze per una matrice dinamica. Prima di tutto, VirtualAlloc viene chiamato per riservare un blocco di pagine con NULL specificato come parametro di indirizzo di base, forzando il sistema a determinare la posizione del blocco. Successivamente, VirtualAlloc viene chiamato ogni volta che è necessario eseguire il commit di una pagina da questa area riservata e l'indirizzo di base della pagina successiva da eseguire il commit viene specificato.
Nell'esempio viene usata la sintassi di gestione delle eccezioni strutturate per eseguire il commit di pagine dall'area riservata. Ogni volta che si verifica un'eccezione di errore di pagina durante l'esecuzione del blocco __try , viene eseguita la funzione di filtro nell'espressione che precede il blocco di __except . Se la funzione filtro può allocare un'altra pagina, l'esecuzione continua nel blocco __try nel punto in cui si è verificata l'eccezione. In caso contrario, viene eseguito il gestore eccezioni nel blocco __except . Per altre informazioni, vedere Gestione delle eccezioni strutturate.
In alternativa all'allocazione dinamica, il processo può semplicemente eseguire il commit dell'intera area anziché riservarla solo. Entrambi i metodi comportano lo stesso utilizzo della memoria fisica perché le pagine di commit non usano alcuna risorsa di archiviazione fisica finché non vengono prima accessibili. Il vantaggio dell'allocazione dinamica è che riduce al minimo il numero totale di pagine di commit nel sistema. Per allocazioni molto grandi, il pre-commit di un'intera allocazione può causare l'esecuzione del sistema di pagine commitable, causando errori di allocazione della memoria virtuale.
La funzione ExitProcess nel blocco __except rilascia automaticamente le allocazioni di memoria virtuale, quindi non è necessario liberare in modo esplicito le pagine quando il programma termina tramite questo percorso di esecuzione. La funzione VirtualFree libera le pagine riservate e commit se il programma viene compilato con la gestione delle eccezioni disabilitata. Questa funzione usa MEM_RELEASE per decommettere e rilasciare l'intera area di pagine riservate e commit.
Nell'esempio C++ seguente viene illustrata l'allocazione dinamica della memoria usando un gestore di eccezioni strutturate.
// A short program to demonstrate dynamic memory allocation
// using a structured exception handler.
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h> // For exit
#define PAGELIMIT 80 // Number of pages to ask for
LPTSTR lpNxtPage; // Address of the next page to ask for
DWORD dwPages = 0; // Count of pages gotten so far
DWORD dwPageSize; // Page size on this computer
INT PageFaultExceptionFilter(DWORD dwCode)
{
LPVOID lpvResult;
// If the exception is not a page fault, exit.
if (dwCode != EXCEPTION_ACCESS_VIOLATION)
{
_tprintf(TEXT("Exception code = %d.\n"), dwCode);
return EXCEPTION_EXECUTE_HANDLER;
}
_tprintf(TEXT("Exception is a page fault.\n"));
// If the reserved pages are used up, exit.
if (dwPages >= PAGELIMIT)
{
_tprintf(TEXT("Exception: out of pages.\n"));
return EXCEPTION_EXECUTE_HANDLER;
}
// Otherwise, commit another page.
lpvResult = VirtualAlloc(
(LPVOID) lpNxtPage, // Next page to commit
dwPageSize, // Page size, in bytes
MEM_COMMIT, // Allocate a committed page
PAGE_READWRITE); // Read/write access
if (lpvResult == NULL )
{
_tprintf(TEXT("VirtualAlloc failed.\n"));
return EXCEPTION_EXECUTE_HANDLER;
}
else
{
_tprintf(TEXT("Allocating another page.\n"));
}
// Increment the page count, and advance lpNxtPage to the next page.
dwPages++;
lpNxtPage = (LPTSTR) ((PCHAR) lpNxtPage + dwPageSize);
// Continue execution where the page fault occurred.
return EXCEPTION_CONTINUE_EXECUTION;
}
VOID ErrorExit(LPTSTR lpMsg)
{
_tprintf(TEXT("Error! %s with error code of %ld.\n"),
lpMsg, GetLastError ());
exit (0);
}
VOID _tmain(VOID)
{
LPVOID lpvBase; // Base address of the test memory
LPTSTR lpPtr; // Generic character pointer
BOOL bSuccess; // Flag
DWORD i; // Generic counter
SYSTEM_INFO sSysInfo; // Useful information about the system
GetSystemInfo(&sSysInfo); // Initialize the structure.
_tprintf (TEXT("This computer has page size %d.\n"), sSysInfo.dwPageSize);
dwPageSize = sSysInfo.dwPageSize;
// Reserve pages in the virtual address space of the process.
lpvBase = VirtualAlloc(
NULL, // System selects address
PAGELIMIT*dwPageSize, // Size of allocation
MEM_RESERVE, // Allocate reserved pages
PAGE_NOACCESS); // Protection = no access
if (lpvBase == NULL )
ErrorExit(TEXT("VirtualAlloc reserve failed."));
lpPtr = lpNxtPage = (LPTSTR) lpvBase;
// Use structured exception handling when accessing the pages.
// If a page fault occurs, the exception filter is executed to
// commit another page from the reserved block of pages.
for (i=0; i < PAGELIMIT*dwPageSize; i++)
{
__try
{
// Write to memory.
lpPtr[i] = 'a';
}
// If there's a page fault, commit another page and try again.
__except ( PageFaultExceptionFilter( GetExceptionCode() ) )
{
// This code is executed only if the filter function
// is unsuccessful in committing the next page.
_tprintf (TEXT("Exiting process.\n"));
ExitProcess( GetLastError() );
}
}
// Release the block of pages when you are finished using them.
bSuccess = VirtualFree(
lpvBase, // Base address of block
0, // Bytes of committed pages
MEM_RELEASE); // Decommit the pages
_tprintf (TEXT("Release %s.\n"), bSuccess ? TEXT("succeeded") : TEXT("failed") );
}