创建 Guard Pages

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

若要创建防护页,请为页面设置 PAGE_GUARD 页保护修饰符。 可以在 VirtualAlloc、VirtualAllocExVirtualProtect 和VirtualProtectEx 函数中指定此值和其他页面保护修饰符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 异常。 第二次尝试成功,因为内存块的防护页保护已被第一次尝试关闭。