第一,分配进程虚拟空间:
VirtualAlloc (PVOID 开始地址,SIZE_T 大小,DWORD 类型,DWORD 保护属性)
“开始地址”可以是NULL,由系统分配进程空间;“类型”是MEM_RESERVE|MEM_PHYSICAL;“保护属性”只能是
PAGE_READWRITE。
MEM_PHYSICAL指的是区域将受物理存储器的支持。
第二,你要计算出分配的页面数目PageCount:
利用本文第二节的GetSystemInfo可以计算出来。
第三,分配物理内存页面:
AllocateUserPhysicalPages (HANDLE 进程句柄,SIZE_T 页数,ULONG_PTR 页面指针数组)
进程句柄可以用GetCurrentProcess()获得;页数是刚计算出来的页数PageCount;页面数组指针unsigned long* Array[PageCount]。
系统会将分配结果存进这个数组。
第四,将物理内存与虚拟空间进行映射:
MapUserPhysicalPages (PVOID 开始地址,SIZE_T 页数,ULONG_PTR 页面指针数组)
“开始地址”是第一步分配的空间;
这样的话,虚拟地址就可以使用了。
如果“页面指针数组”是NULL,则取消映射。
第五,释放物理页面
FreeUserPhysicalPages (HANDLE 进程句柄,SIZE_T 页数,ULONG_PTR 页面指针数组)
这个除了释放物理页面外,还会取消物理页面的映射。
第六,释放进程空间
VirtualFree (PVOID 开始地址,0,MEM_RELEASE)
C++程序:
首先,在登录用户有了Lock Pages in Memory权限以后,还需要调用Windows API激活这个权限。
BOOL VirtualMem::LoggedSetLockPagesPrivilege ( HANDLE hProcess,BOOL bEnable)
{
struct {
DWORD Count;//数组的个数
LUID_AND_ATTRIBUTES Privilege [1];} Info;
HANDLE Token;
//打开本进程的权限句柄
BOOL Result = OpenProcessToken ( hProcess,
TOKEN_ADJUST_PRIVILEGES,
& Token);
If (Result!= TRUE )
{
printf( "Cannot open process token.\n" );
return FALSE;
}
//我们只改变一个属性
Info.Count = 1;
//准备激活
if( bEnable )
Info.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
else
Info.Privilege[0].Attributes = 0;
//根据权限名字找到LGUID
Result = LookupPrivilegeValue ( NULL,
SE_LOCK_MEMORY_NAME,
&(Info.Privilege[0].Luid));
if( Result != TRUE )
{
printf( "Cannot get privilege for %s.\n", SE_LOCK_MEMORY_NAME );
return FALSE;
}
// 激活Lock Pages in Memory权限
Result = AdjustTokenPrivileges ( Token, FALSE,(PTOKEN_PRIVILEGES) &Info,0, NULL, NULL);
if( Result != TRUE )
{
printf ("Cannot adjust token privileges (%u)\n", GetLastError() );
return FALSE;
}
else
{
if( GetLastError() != ERROR_SUCCESS )
{
printf ("Cannot enable the SE_LOCK_MEMORY_NAME privilege; ");
printf ("please check the local policy.\n");
return FALSE;
}
}
CloseHandle( Token );
return TRUE;
}
分配100M虚拟空间:
PVOID pVirtual=VirtualAlloc(NULL,100*1024*1024,MEM_RESERVE|MEM_PHYSICAL,PAGE_READWRITE);
if(pVirtual==NULL)
cout<<"没有那么大连续进程空间!"<<endl;
MEMORYSTATUS memStatusVirtual5;
GlobalMemoryStatus(&memStatusVirtual5);
cout<<"虚拟内存分配:"<<endl;
cout<<"减少物理内存="<<memStatusVirtual4.dwAvailPhys-memStatusVirtual5.dwAvailPhys<<endl
cout<<"减少可用页文件="<<memStatusVirtual4.dwAvailPageFile- memStatusVirtual5.dwAvailPageFile<<endl;
cout<<"减少可用进程空间="
<<memStatusVirtual4.dwAvailVirtual-memStatusVirtual5.dwAvailVirtual<<endl<<endl;
结果如下:
可以看见,只分配了进程空间,没有分配物理内存。
分配物理内存:
ULONG_PTR pages=(ULONG_PTR)100*1024*1024/sysInfo.dwPageSize;
ULONG_PTR *frameArray=new ULONG_PTR[pages];
//如果没激活权限,是不能调用这个方法的,可以调用,但是返回FALSE
BOOL flag=AllocateUserPhysicalPages(GetCurrentProcess(),
&pages,frameArray);
if(flag==FALSE)
cout<<"分配物理内存失败!"<<endl;
MEMORYSTATUS memStatusVirtual6;
GlobalMemoryStatus(&memStatusVirtual6);
cout<<"物理内存分配:"<<endl;
cout<<"减少物理内存="<<memStatusVirtual5.dwAvailPhys-memStatusVirtual6.dwAvailPhys<<endl
cout<<"减少可用页文件="<<memStatusVirtual5.dwAvailPageFile-memStatusVirtual6.dwAvailPageFile<<endl;
cout<<"减少可用进程空间="<<memStatusVirtual5.dwAvailVirtual-memStatusVirtual6.dwAvailVirtual<<endl<<endl;
结果如下:
减少物理内存分配:
减少物理内存=105037824
减少可用页文件=104976384
减少可用进程空间=1048576
分配了物理内存,可能分配时需要进程空间管理。
物理内存映射进程空间:
int* pVInt=(int*)pVirtual;
//pVInt[0]=10;这时候访问会出错
flag=MapUserPhysicalPages(pVirtual,1,frameArray);
if(flag==FALSE)
cout<<"映射物理内存失败!"<<endl;
MEMORYSTATUS memStatusVirtual7;
GlobalMemoryStatus(&memStatusVirtual7);
cout<<"物理内存分配:"<<endl;
cout<<"减少物理内存="<<memStatusVirtual6.dwAvailPhys-memStatusVirtual7.dwAvailPhys<<endl
cout<<"减少可用页文件="<<memStatusVirtual6.dwAvailPageFile-memStatusVirtual7.dwAvailPageFile<<endl;
cout<<"减少可用进程空间="
<<memStatusVirtual6.dwAvailVirtual-memStatusVirtual7.dwAvailVirtual<<endl<<endl;
结果如下:
物理内存分配:
减少物理内存=0
减少可用页文件=0
减少可用进程空间=0
这个过程没有损失任何东西。
看看第一次映射和第二次映射的值:
pVInt[0]=10;
cout<<"第一次映射值="<<pVInt[0]<<endl;
flag=MapUserPhysicalPages(pVirtual,1,frameArray+1);
if(flag==FALSE)
cout<<"映射物理内存失败!"<<endl;
pVInt[0]=21;
cout<<"第二次映射值="<<pVInt[0]<<endl;
flag=MapUserPhysicalPages(pVirtual,1,frameArray);
if(flag==FALSE)
cout<<"映射物理内存失败!"<<endl;
cout<<"再现第一次映射值="<<pVInt[0]<<endl;
结果如下:
第一次映射值=10
第二次映射值=21
再现第一次映射值=10
可以看出,第二次映射的值没有覆盖第一次映射的值,也就是说,用同一个进程空间地址可以取出两份数据,这样的话,相当于进程的地址空间增大了。(完)