应用程序通过设置内存页面的PAGE_GUARD页面保护修饰符标志来建立【守卫页】.可以在函数VirtualAlloc,VirtualProtect和VirtualProtectEx中指定此标志以及其他页面保护标志。除了NO_ACCESS标志之外,PAGE_GUARD标志可以与任何其他页面保护标志一起使用。
如果程序尝试访问保护页面中的地址,则操作系统将引发STATUS_GUARD_PAGE(0x80000001)异常。操作系统还清除PAGE_GUARD标志,删除内存页面的保护页面状态。系统不会停止下一次使用STATUS_GUARD_PAGE异常访问内存页的尝试。
如果在系统服务期间发生保护页异常,则服务失败,并通常返回一些故障状态指示符。由于系统还会删除相关的内存页面的保护页面状态,因为STATUS_GUARD_PAGE异常会导致同一系统服务的下次调用不会失败(除非当然有人重新建立了保护页面)。
因此,防护页面提供用于存储器页面访问的单触发警报。这对于需要监视大型动态数据结构的增长的应用程序可能是有用的。例如,有操作系统使用防护页面来实现自动堆栈检查。
以下简短程序说明了保护页面保护的单次操作,以及如何导致系统服务失败:
#include < windows.h >
#include < stdio.h >
#include < stdlib.h >
int main()
{
//局部变量
LPVOID lpvAddr;
DWORD cbSize;
BOOL vLock;
LPVOID commit;
//我们要分配的内存量
cbSize = 512;
//尝试分配一些内存
lpvAddr = VirtualAlloc(NULL,cbSize,MEM_RESERVE,PAGE_NOACCESS);
//如果我们失败了...
if(lpvAddr == NULL)
fprintf(stdout,“VirtualAlloc failed on RESERVE with%ld \\ n”,
GetLastError());
//尝试提交分配的内存
commit = VirtualAlloc(NULL,cbSize,MEM_COMMIT,PAGE_READONLY|PAGE_GUARD);
//如果我们失败了...
if(commit == NULL)
fprintf(stderr,“VirtualAlloc failed on COMMIT with%ld \\ n”,
GetLastError());
else //我们成功了
fprintf(stderr,“Committed%lu bytes at address%lp \\ n”,
cbSize,commit);
//尝试锁定已提交的内存
vLock = VirtualLock(commit,cbSize);
//如果我们失败了...
如果(!vLock)
fprintf(stderr,"Cannot lock at %lp, error = %lu\n",
commit,GetLastError());
else //我们成功了
fprintf(stderr,"Lock Achieved at %lp\n",commit);
//尝试再次锁定提交的内存
vLock = VirtualLock(commit,cbSize);
//如果我们失败了...
如果(!vLock)
fprintf(stderr,"Cannot get 2nd lock at %lp, error = %lu\n",
commit,GetLastError());
else //我们成功了
fprintf(stderr,"2nd Lock Achieved at %lp\n",commit);
} // endof函数
该程序的输出如下所示:
在地址003F0000处提交512字节
Cannot lock at 003F0000, error = 0x80000001
第二锁实现在003F0000
请注意,首次尝试锁定内存块失败,会引发STATUS_GUARD_PAGE异常。第二次尝试成功,因为第一次尝试已经切换了内存块的保护页面保护。