Udostępnij za pośrednictwem


Rezerwowanie i przydzielanie pamięci

Poniższy przykład ilustruje użycie funkcji VirtualAlloc i VirtualFree w rezerwowaniu i zatwierdzaniu pamięci zgodnie z potrzebami dla tablicy dynamicznej. Najpierw wywoływana jest funkcja VirtualAlloc, aby zarezerwować blok stron z NULL jako parametrem adresu podstawowego, co zmusza system do określenia lokalizacji bloku. Później VirtualAlloc jest wywoływana za każdym razem, gdy jest konieczne zatwierdzenie strony z tego regionu zarezerwowanego, a adres podstawowy następnej strony do zatwierdzenia jest określony.

W przykładzie użyto składni obsługi wyjątków strukturalnych do zatwierdzania stron z regionu zarezerwowanego. Za każdym razem, gdy wystąpi wyjątek błędu strony podczas wykonywania bloku __try, funkcja filtra w wyrażeniu poprzedzającym blok __except jest wykonywana. Jeśli funkcja filter może przydzielić inną stronę, wykonanie będzie kontynuowane w bloku __try w momencie wystąpienia wyjątku. W przeciwnym razie wykonywana jest procedura obsługi wyjątków w bloku __except. Aby uzyskać więcej informacji, zobacz Obsługa wyjątków strukturalnych.

Jako alternatywa dla alokacji dynamicznej proces może po prostu zatwierdzić cały region zamiast rezerwować go tylko. Obie metody powodują użycie tej samej pamięci fizycznej, ponieważ zatwierdzone strony nie zużywają żadnego magazynu fizycznego do momentu pierwszego uzyskania do nich dostępu. Zaletą dynamicznej alokacji jest zminimalizowanie całkowitej liczby zatwierdzonych stron w systemie. W przypadku bardzo dużych alokacji, wstępne zatwierdzenie całej alokacji może spowodować, że systemowi skończą się strony gotowe do alokacji, co spowoduje błędy podczas alokacji pamięci wirtualnej.

Funkcja ExitProcess w bloku __except automatycznie zwalnia przydzieloną pamięć wirtualną, więc nie ma potrzeby jawnego zwalniania stron, gdy program kończy działanie tą ścieżką wykonania. Funkcja VirtualFree zwalnia zastrzeżone i zatwierdzone strony, jeśli program został skompilowany z wyłączoną obsługą wyjątków. Ta funkcja używa MEM_RELEASE do odblokowania i zwolnienia całego regionu zarezerwowanych i zatwierdzonych stron.

W poniższym przykładzie języka C++ pokazano alokację pamięci dynamicznej przy użyciu procedury obsługi wyjątków strukturalnych.

// 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") );

}