创建 Guard 页面

保护页为内存页访问提供一次性警报。 这对于需要监视大型动态数据结构增长的应用程序非常有用。 例如,有些操作系统使用保护页来实现自动堆栈检查。

若要创建保护页,请为该页设置 PAGE_GUARD 页保护修饰符。 可以在 VirtualAlloc、VirtualAllocExVirtualProtectVirtualProtectEx 函数中指定此值以及其他页面保护修饰符PAGE_GUARD修饰符可以与其他任何页面保护修饰符一起使用,PAGE_NOACCESS除外。

如果程序尝试访问保护页中的地址,系统会引发 STATUS_GUARD_PAGE_VIOLATION (0x80000001) 异常。 系统还会清除 PAGE_GUARD 修饰符,从而删除内存页的防护页状态。 系统不会停止下次尝试访问内存页时出现 STATUS_GUARD_PAGE_VIOLATION 异常。

如果在系统服务期间发生保护页异常,则服务会失败,并且通常会返回一些故障状态指示器。 由于系统还会删除相关内存页的防护页状态,因此下一次调用同一系统服务不会因 STATUS_GUARD_PAGE_VIOLATION 异常 (失败,除非有人) 重新建立保护页。

以下简短程序演示了保护页面保护的行为。

/* A program to demonstrate the use of guard pages of memory. Allocate
   a page of memory as a guard page, then try to access the page. That
   will fail, but doing so releases the lock on the guard page, so the
   next access works correctly.

   The output will look like this. The actual address may vary.

   This computer has a page size of 4096.
   Committed 4096 bytes at address 0x00520000
   Cannot lock at 00520000, error = 0x80000001
   2nd Lock Achieved at 00520000

   This sample does not show how to use the guard page fault to
   "grow" a dynamic array, such as a stack. */

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>

int main()
{
  LPVOID lpvAddr;               // address of the test memory
  DWORD dwPageSize;             // amount of memory to allocate.
  BOOL bLocked;                 // address of the guarded memory
  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;

  // Try to allocate the memory.

  lpvAddr = VirtualAlloc(NULL, dwPageSize,
                         MEM_RESERVE | MEM_COMMIT,
                         PAGE_READONLY | PAGE_GUARD);

  if(lpvAddr == NULL) {
    _tprintf(TEXT("VirtualAlloc failed. Error: %ld\n"),
             GetLastError());
    return 1;

  } else {
    _ftprintf(stderr, TEXT("Committed %lu bytes at address 0x%lp\n"),
              dwPageSize, lpvAddr);
  }

  // Try to lock the committed memory. This fails the first time 
  // because of the guard page.

  bLocked = VirtualLock(lpvAddr, dwPageSize);
  if (!bLocked) {
    _ftprintf(stderr, TEXT("Cannot lock at %lp, error = 0x%lx\n"),
              lpvAddr, GetLastError());
  } else {
    _ftprintf(stderr, TEXT("Lock Achieved at %lp\n"), lpvAddr);
  }

  // Try to lock the committed memory again. This succeeds the second
  // time because the guard page status was removed by the first 
  // access attempt.

  bLocked = VirtualLock(lpvAddr, dwPageSize);

  if (!bLocked) {
    _ftprintf(stderr, TEXT("Cannot get 2nd lock at %lp, error = %lx\n"),
              lpvAddr, GetLastError());
  } else {
    _ftprintf(stderr, TEXT("2nd Lock Achieved at %lp\n"), lpvAddr);
  }

  return 0;
}

首次尝试锁定内存块失败,引发 STATUS_GUARD_PAGE_VIOLATION 异常。 第二次尝试成功,因为第一次尝试已关闭内存块的防护页保护。