(转载)浅议Windows 2000/XP Pagefile组织管理

来源:互联网 发布:简单的html源码下载 编辑:程序博客网 时间:2024/06/01 14:56

(转载)浅议Windows 2000/XP Pagefile组织管理

Posted on 2008-07-08 14:27 Da Vinci 阅读(242) 评论(1)  编辑 收藏 网摘 所属分类: OS&Kernel
    任何时候系统内存资源相对磁盘空间来说都是相形见拙的。因为虚拟内存机制,使我们可以有相对丰富的地址资源(通常32bit的虚拟地址,可以有4G的寻址空间),而这些资源对物理内存来说一般情况是总是绰绰有余的。所以在现代操作系统中,总是在相对紧张时使用一些策略,如FIFO、LRU等将物理内存的一些页面置入相对便宜的磁盘空间资源中。一般的UNIX系统,独立使用一个分区,即swap partition。而这方面Windows只是使用普通的文件,通常命名为pagefile.sys,位于各分区的根目录中。由于受到用于pagefile的PTE的限制(PTE中使用4bit来识别操作的 pagefile),所以Windows最多可以支持16个pagefile.sys。

    从上描述,pagefile.sys本身就是一个比较特殊的文件,根据系统的情况它的大小是可扩展的,通常我们可以使用“控制面板”的“系统”小Applet来设置。由于其特殊性, Windows在启动阶段会对每个pagefile.sys建立相应的FILE_OBJECT,并且设置SharedRead字段为False,而且在 System进程,每个FILE_OBJECT都分别有一个句柄指向,这样即只允许系统自身对其操作,避免用户对其进行删除等误操作。

     为了对pagefile.sys进行管理,Windows中有一个长度为16的数组,用于对pagefile.sys的组织。每个成员分别对应一个 pagefile。这个数组由系统变量MmPagingFile指向,每个成员都是一个指向MMPAGING_FILE的结构,这个结构有如下的格式:

    +0x000 Size             : Uint4B
    +0x004 MaximumSize      : Uint4B
    +0x008 MinimumSize      : Uint4B
    +0x00c FreeSpace        : Uint4B
    +0x010 CurrentUsage     : Uint4B
    +0x014 PeakUsage        : Uint4B
    +0x018 Hint             : Uint4B
    +0x01c HighestPage      : Uint4B
    +0x020 Entry            : [2] Ptr32 _MMMOD_WRITER_MDL_ENTRY
    +0x028 Bitmap           : Ptr32 _RTL_BITMAP
    +0x02c File             : Ptr32 _FILE_OBJECT
    +0x030 PageFileName     : _UNICODE_STRING
    +0x038 PageFileNumber   : Uint4B
    +0x03c Extended         : UChar
    +0x03d HintSetToZero    : UChar
    +0x03e BootPartition    : UChar
    +0x040 FileHandle       : Ptr32 Void

通过这个结构,我们可以很容易的得到相应pagefile的使用情况(MaximumSize、MinimumSize、FreeSpace、 CurrentUsage、PeakUsage,请参阅windbg的!vm命令),其对应的FILE_OBJECT等。另外通过FILE_OBJECT 的DeviceObject与Vpb字段,我们就可知道这个pagefile所处的分区及分区使用的文件系统等等信息。我们来详细介绍一下Bitmap成员。

    Bitmap是一个RTL_BITMAP的结构,其定义在ntddk.h中:

    typedef struct _RTL_BITMAP {
        ULONG SizeOfBitMap;                     // Number of bits in bit map
        PULONG Buffer;                          // Pointer to the bit map itself
    } RTL_BITMAP;

     与页框数据库(Pfn Database)与虚拟内存(x86平台PAGE_SIZE 4k)一样,Windows也将pagefile分割成4K一块块的大小,称为一页,每页由Bitmap对应的1 bit指定状态。1为占用,0为空闲。通过使用RtlFindClearBits或是 RtlFindClearBitsAndSet等函数对Bitmap进行操作,寻找这些文件的未用页面。虽然Bitmap指明占用状态时,Windows 常以4k为单位,但为了提高性能,Windows在一次写pagefile的单位通常为64k(MmModifiedWriteClusterSize个页)。还有MMMOD_WRITER_MDL_ENTRY,等我下面提及相关内容时再加以说明。

    先用windbg来消化一下上面的讨论:

    kd> dd MmPagingFile l 10  //从输出结果可以看出我的机子上设了两个pagefile。
    80547020  80d2af80 feec1548 00000000 00000000
    80547030  00000000 00000000 00000000 00000000
    80547040  00000000 00000000 00000000 00000000
    80547050  00000000 00000000 00000000 00000000
    kd> dd @$p l 40   //第一个pagefile的情况。
    80d2af80  00006400 0000c800 00006400 00000c38
    80d2af90  000057c7 000057c7 00000000 00000000
    80d2afa0  feea1cb8 feea1c18 fecbb000 feddc428
            .
            .
            .

    kd> dd feddc428 l 4  //从上面给出的MMPAGING_FILE,很容易得到file object(Offset 0x2c)。
    feddc428  00700005 80ecf2f0 80ecf268 fee66c10

    kd> !devobj 80ecf2f0   //aFILE_OBJECT的结构在ntddk.h中给出,其第三个dword就是DEVICE_OBJECT。
    Device object (80ecf2f0) is for:
     HarddiskVolume2 /Driver/Ftdisk DriverObject 80d97030
    Current Irp 00000000 RefCount 1316 Type 00000007 Flags 00001150
    Vpb 80ecf268 Dacl e13d1484 DevExt 80ecf3a8 DevObjExt 80ecf490 Dope 80ecf210 DevNode 80d95bd0 
    ExtensionFlags (0000000000)  
    AttachedDevice (Upper) 80d954b8 /Driver/VolSnap
    Device queue is not busy.

    另外FILE_OBJECT的第四个dword(fee66c10)就是VPB结构,你使用!vpb分析分析,限于篇幅,我就不在这儿列出了。

    通过上面windbg的分析,我们已经基本上对pagefile有了一定的了解了,下面转入内存子系列与IO子系统(调用FSD)对pagefile的组织管理。

     通常情况下,对于进程可见的永远是虚拟地址,存取某个虚拟地址,对于不存在的地址(对于X86,即其PTE的P位为0),通过触发硬件中断(X86为 int e),由软件来对这些PTE进行解析,譬如原型PTE(我在《探究Windows 2000/XP原型PTE》中详细介绍),或是过渡PTE (Transition PTE,某些页面由于进程工作集修整等原因,成为可被使用的页面,但这些页面的内容当前对这些进程仍有效,可随时重新使用,所以 Windows使用Transition这个术语区别于纯粹的Free或Zeroed列表,我在《解析Winndows 2000/XP物理内存管理》中提及PFN列表)等,而对于Page File,实际上也有一个对应的pte指向相应pagefile.sys,完成解析工作 (MiResolvePageFileFault),处理页面错误(通过IoPageRead,下面会介绍)。

    所以在继续讨论之前我们来说明一下Pagefile PTE,它的格式如下:

    Valid            : Pos 0, 1 Bit
    PageFileLow      : Pos 1, 4 Bits
    Protection       : Pos 5, 5 Bits
    Prototype        : Pos 10, 1 Bit
    Transition       : Pos 11, 1 Bit
    PageFileHigh     : Pos 12, 20 Bits

     对于Prototype PTE与Transition PTE,总有1bit用于识别相应的PTE,如上的Prototype字段,但对于 PageFile PTE,却没有对应的识别bit,实际上MiDispatchFault(由KiTrap0E调用),是在解析完 Prototype PTE(MiResolveProtoPteFault)、Transition PTE (MiResolveTransitionFault)、还有MiResolveDemandZeroFault后,才调用 MiResolvePageFileFault的,当然在MiResolveProtoPteFault处理过程中也是最后调用 MiResolvePageFileFault的。

    假设我们存取一个当前驻留在pagefile中的页面,通过 MiDispatchFault,控制权转到MiResolvePageFileFault后,他会根据PTE的PageFileLow来索引 MMPAGING_FILE数组,即判断这一页面位于哪个pagefile.sys中,因为PageFileLow为4个bit,所以Windows最多可以支持16个pagefile.sys。这样内存子系统根据这个索引从MmPagingFile中描述的页文件结构取出这个pagefile的 FILE_OBJECT(上面介绍过)。加上PageFileHigh所指定的pagefile.sys的偏移值, MiResolvePageFileFault通过返回一个值为0xC0033333的特殊NTSTATUS通知MiDispatchFault调用 IoPageRead得到此页面。IoPageRead的原型如下(定义于ntifs.h中):

    NTKERNELAPI
    NTSTATUS
    IoPageRead(
        IN PFILE_OBJECT FileObject,
        IN PMDL MemoryDescriptorList,
        IN PLARGE_INTEGER StartingOffset,
        IN PKEVENT Event,
        OUT PIO_STATUS_BLOCK IoStatusBlock
    ); 

     当然在调用IoPageRead之前,内存管理器必须分配一个物理页面,必要的时候还要调用MiRemoveAnyPage腾出空间,然后调用 MiInitializeReadInProgressPfn,将这一页框置成ReadInProgress状态,然后将IoPageRead所需要的 MDL参数MemoryDescriptorList指向这一页面。MDL的虚拟地址字段也就是IoPageRead读入的页面映射的虚拟地址,也即满足我们先前假设的页面错误。

    IoPageRead实际上通过Allocate一个IRP,用DIRECT_IO的方式(即我们提供的MDL),然后设置一个Complete Routine,用于取消页面读取之前的ReadInProgress状态,再通过IoCallDriver 调用IO子系统调用对应的File System Driver(通常由FILE_OBJECT的VPB参数确定),至于FSD是如何读取 pagefile.sys的,这儿不加以讨论,ntifs提供的fastfat的源代码是学习的方向。

    需要指出的是 IoPageRead是一个同步操作,即只有等待页面读完毕以后才可以往下处理。这也是MiDispatchFault只能运行于 DISPATCH_LEVEL IRQL之下的主要原因。IoPageRead通过设备分配的IRP的 IRP_SYNCHRONOUS_PAGING_IO的标志来实现同步的。另外他也设置了IRP_PAGING_IO、IRP_NOCACHE标志,用于与FSD之间的特殊通信要求。

    由于工作集修整等的需要,通过MiModifiedPageWriter(MPW)线程实行将某些页面置入pagefile中。MPW使用MMPAGING_FILE结构的_MMMOD_WRITER_MDL_ENTRY类型的Entry进行操作, _MMMOD_WRITER_MDL_ENTRY不仅仅由MiModifiedPageWriter使用,他还要让MiMappedPageWriter 使用(用于Mapped file),所以_MMMOD_WRITER_MDL_ENTRY结构不仅函有MDL成员,还包含Control Area等等。限于篇幅,我不将其结构列出。MPW通过IoAsynchronousPageRead将页面按前面说的一次 MmModifiedWriteClusterSize个页面写入pagefile中。对于IoAsynchronousPageRead其使用的 IRP flag是IRP_PAGING_IO与IRP_NOCACHE,说明他是异步操作的。这也可从他的名字看出,区别于Windows提供的另一个相关过程IoSynchronousPageWrite,他是同步的。

    讲到这儿,对于page file的组织管理的一些基本的印象应该是有的。最后需要指出的一点是,对于IoPageRead不仅仅是对于pagefile的,其也可以针对mapped file,还有 MiModifiedPageWriter,要不是避免死锁也不会区分出MiMappedPageWriter,实际上Windows内部内存管理器对于 pagefile与mappedfile的管理使用是基本相同的,而FSD的处理也只是一点点的区别而已。所以结合我以前介绍的如 Control Area等概念,对mapped file等的理解也是可以参照本文的。还有同样的一句话,错误地方希望得到你的指点。
原创粉丝点击