浅议Windows 2000/XP Pagefile组织管理

转自:http://www.cnblogs.com/Sonic2007/archive/2008/07/08/1238167.htmlhtml

任什么时候候系统内存资源相对磁盘空间来讲都是相形见拙的。由于虚拟内存机制,使咱们能够有相对丰富的地址资源(一般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等的理解也是能够参照本文的。还有一样的一句话,错误地方但愿获得你的指点。数组

相关文章
相关标签/搜索