3.1.3 看对于“肮脏”页面的处理
3.1.3 看对于“肮脏”页面的处理
文章目录
- 3.1.3 看对于“肮脏”页面的处理
- 再看对于“肮脏”页面的处理
- MmPageOutVirtualMemory()
再看对于“肮脏”页面的处理
MmPageOutVirtualMemory()
NTSTATUS
NTAPI
MmPageOutVirtualMemory(PMADDRESS_SPACE AddressSpace,
PMEMORY_AREA MemoryArea,
PVOID Address,
PMM_PAGEOP PageOp)
{
PFN_TYPE Page;
BOOLEAN WasDirty;
SWAPENTRY SwapEntry;
NTSTATUS Status;
DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
Address, AddressSpace->Process->UniqueProcessId);
/*
* Check for paging out from a deleted virtual memory area.
*/
if (MemoryArea->DeleteInProgress)
{
PageOp->Status = STATUS_UNSUCCESSFUL;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_UNSUCCESSFUL);
}
/*
* Disable the virtual mapping.
*/
MmDisableVirtualMapping(AddressSpace->Process, Address,
&WasDirty, &Page);
if (Page == 0)
{
KEBUGCHECK(0);
}
/*
* Paging out non-dirty data is easy.
*/
if (!WasDirty)
{
MmLockAddressSpace(AddressSpace);
MmDeleteVirtualMapping(AddressSpace->Process, Address, FALSE, NULL, NULL);
MmDeleteAllRmaps(Page, NULL, NULL);
if ((SwapEntry = MmGetSavedSwapEntryPage(Page)) != 0)
{
MmCreatePageFileMapping(AddressSpace->Process, Address, SwapEntry);
MmSetSavedSwapEntryPage(Page, 0);
}
MmUnlockAddressSpace(AddressSpace);
MmReleasePageMemoryConsumer(MC_USER, Page);
PageOp->Status = STATUS_SUCCESS;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_SUCCESS);
}
/*
* If necessary, allocate an entry in the paging file for this page
*/
SwapEntry = MmGetSavedSwapEntryPage(Page);//获取该物理页面的倒换描述项
//页面是脏的
if (SwapEntry == 0)//分配失败
{
SwapEntry = MmAllocSwapPage();
if (SwapEntry == 0)
{
MmShowOutOfSpaceMessagePagingFile();
MmEnableVirtualMapping(AddressSpace->Process, Address);//恢复原来的映射
PageOp->Status = STATUS_UNSUCCESSFUL;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_PAGEFILE_QUOTA);//因倒换页面不够而失败
}
}
/*
* Write the page to the pagefile
*/
Status = MmWriteToSwapPage(SwapEntry, Page);//将物理页面的内容写入倒换页面
if (!NT_SUCCESS(Status))
{//写文件失败
DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
Status);
MmEnableVirtualMapping(AddressSpace->Process, Address);//恢复原来的映射
PageOp->Status = STATUS_UNSUCCESSFUL;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_UNSUCCESSFUL);
}
/*
* Otherwise we have succeeded, free the page1 写倒换文件成功
*/
DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page << PAGE_SHIFT);
MmLockAddressSpace(AddressSpace);
MmDeleteVirtualMapping(AddressSpace->Process, Address, FALSE, NULL, NULL);//撤销通往目标物理页面的映射
MmCreatePageFileMapping(AddressSpace->Process, Address, SwapEntry);//建立(虚存页面)通往倒换文件的映射
MmUnlockAddressSpace(AddressSpace);
MmDeleteAllRmaps(Page, NULL, NULL);
MmSetSavedSwapEntryPage(Page, 0);//该物理页面不再映射到倒换文件
MmReleasePageMemoryConsumer(MC_USER, Page);//释放该物理页面
PageOp->Status = STATUS_SUCCESS;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_SUCCESS);
}
先通过 MmGetSavedSwapEntryPage()获取(本页面)在页面倒换文件中的页面号。如果本页面在倒换文件中尚无映像,就通过MmAllocSwapPage()加以分配。然后就由MmWriteToSwapPage()将物理页面的内容写入倒换文件。此后的操作就无须赘述了,不过需要说明一点,就是MmCreatePageFileMapping()在将SwapEntry写入相应虚存页面的PTE,时要将其左移一位,使得PTE的最低位即PA_PRESENT位为0,说明该页面不在物理内存中。在哪里呢?只要整个PTE非0,就说明在某个倒换文件的某个页面中,此时的高31位就是倒换文件号与页面号的组合。显然,这个组合不能为0,这就是为什么这个组合中的页面号实际上是(文件内)页面号加1的原因。
关于页面映射表,还要作些说明。当内核调度一个进程运行时,需要将控制寄存器CR3设置成指向这个进程的页面映射表,此时使用的是页面映射表的物理地址,这样MMU才能找到当前进程的页面映射表,并进而将具体的表项PTE装入其高速缓存TLB。但是,如果是CPU要访问当前进程的页面映射表,则需使用其虚拟地址。不管是什么进程,其页面映射表在虚存空间的位置都是固定的,都是 0xc0000000。凡是在程序中需要访问本进程的页面映射表时,总是用这个地址作为起点。当然,对于不同的当前进程,这个虚拟地址会映射到不同的物理页面中,不同进程的页面映射表所在的物理页面当然是不同的。