当前位置: 首页 > article >正文

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。凡是在程序中需要访问本进程的页面映射表时,总是用这个地址作为起点。当然,对于不同的当前进程,这个虚拟地址会映射到不同的物理页面中,不同进程的页面映射表所在的物理页面当然是不同的。


http://www.kler.cn/a/369362.html

相关文章:

  • 在不支持AVX的linux上使用PaddleOCR
  • 使用pyinstaller将python代码打包为exe程序
  • BUUCTF之web篇
  • Java题集练习4
  • 移植rv1106SDK的ipcweb到ubuntu
  • Elasticsearch的实战应用
  • 介绍 Docker 的基本概念和优势,以及在应用程序开发中的实际应用。(AI)
  • Spring-Day3
  • Redis 哨兵 问题
  • 深入理解 SQL 中的 WITH AS 语法
  • Flutter TextField和Button组件开发登录页面案例
  • Spark入门到实践
  • Codeforces Round 981 (Div. 3) A-D
  • JDK的下载
  • [C++]——红黑树(附源码)
  • 基于人脸识别系统设计与仿真-基于matlab
  • qt QApplication详解
  • labelimg使用教程
  • 【数据结构】二叉树——堆
  • 【STM32】SD卡
  • SpringBoot支付回调枚举+策略+工厂模式
  • python构建flask服务用于视频文件的处理后返回
  • 基于单片机的家用多功能衣柜控制系统设计
  • Excel重新踩坑4:快捷键;逻辑函数;文本函数;日期相关函数;查找与引用函数;统计类函数;数组公式
  • ESP32 S3 怎么开发基于ESP-RTC的音视频实时交互的应用,用语AI陪伴的领域
  • 爬虫爬取数据时,如何解决由于验证码通常是动态生成的,直接通过URL下载可能会遇到验证码内容不一致的问题?( ̄︶ ̄)↗