windows下MmAccessFault->MiDispatchFault处理过程说明

页错误的时候,先处理有效的情况,例如copy-on-write

二.无效情况:

MmAccessFault函数中查看到faultaddress对应的pte无效之时,查看TempPte.u.Soft.Prototype,

1 判断原型pte 

if (TempPte.u.Soft.Prototype != 0) {

如果是指向原型PTE,就要找出对应的原型PTE

            PointerProtoPte = MiPteToProto (&TempPte); //这个是直接在PTE中取出来

            if ((SessionAddress == TRUE) &&

                (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)) {

//这个是从VAD树里面找出来

                PointerProtoPte = MiCheckVirtualAddress (VirtualAddress,

                                                         &ProtectionCode,

                                                         &ProtoVad);

  这

如果是,则获取对应的ppte做为 PointerProtoPte 

MiDispatchFault (

    IN ULONG_PTR FaultStatus,

    IN PVOID VirtualAddress,

    IN PMMPTE PointerPte,

    IN PMMPTE PointerProtoPte,

    IN LOGICAL RecheckAccess,

    IN PEPROCESS Process,

    IN PVOID TrapInformation,

    IN PMMVAD Vad

    )

最后不管什么情况,就去调用MiDispatchFault来处理page fault

下面是MiDispatchFault中的处理情况:

1。原型PTE,当PointerProtoPte不为空时,说明这是一个原型PTE,调用MiResolveProtoPteFault来处理

else TempPte = *PointerPte;

2 ,transition状态的PTE 

        else if ((TempPte.u.Soft.Transition == 0) &&

                 (TempPte.u.Soft.Protection == 0)) {

调用 MiResolveTransitionFault 来解决

3。 

        else if (TempPte.u.Soft.PageFileHigh == 0) {

            //

            // Demand zero fault.

            //

            status = MiResolveDemandZeroFault (VirtualAddress,

 4。

 else {

            //

            // Page resides in paging file.

            //

MiResolvePageFileFault

在调用 MiResolveProtoPteFault 处理原型PTE时候,也要根据判断原型PTE是否有效来处理

原型PTE有效的话,直接调用MiCompleteProtoPteFault 完成处理

否则无效时,又对应下面4种情况:

    // Make the prototype PTE valid, the prototype PTE is in

    // one of these 4 states:

    //

    //   demand zero

    //   transition

    //   paging file

    //   mapped file 

//下面的判断是按顺序来的。

//if (TempPte.u.Soft.Prototype == 1) {MiResolveMappedFileFault//这个是用subsection来读取

    // Calculate address of subsection for this prototype PTE.

    // //PointerPte里面记录的是subsection的偏移(相对MmSubsectionBase),MmSubsectionBase =MmNonPagedPoolstart

    Subsection = MiGetSubsectionAddress (PointerPte);

else if (TempPte.u.Soft.Transition == 1) {MiResolveTransitionFault //本身里面的物理页还在内存中。只是暂时无效而已

else if (TempPte.u.Soft.PageFileHigh == 0) {MiResolveDemandZeroFault//找一块物理内存,清空,然后让原来的pte有效并指向这块物理内存

else {

        //

        // Paging file.

        //

  ////TempPte.u.Soft.PageFileHigh便是在page中的偏移,但只有20位,这是页编号,按页对齐,所以//TempPte.u.Soft.PageFileHigh<<12后才是准确的offset

//所以pagefile最大也是只支持4G,windows internals这书里面也有提到

        status = MiResolvePageFileFault (FaultingAddress,

                                         PointerProtoPte,  

    //

 //////////////////////////////////////

在处理有效的原型PTE时调用MiCompleteProtoPteFault。

里面只使用了PointerProtoPte原型PTE获取对应的PageFrameIndex,然后就设置给相应的va的pte,

        MI_MAKE_VALID_USER_PTE (TempPte,

                                PageFrameIndex,

                                Protection,

                                PointerPte);

PageFrameIndex并不是从pfn的"原始PTE内容:"取的

无效原型PTE时,也只是从原型PTE获取Subsection,并不是用了pfn中的原始内容,这里和书上说的有出入。奇怪

。有了subsection后,就能能进来了

    //

    // Calculate address of subsection for this prototype PTE.

    //

    Subsection = MiGetSubsectionAddress (PointerPte);

================================================

另外,这里也有一个地方说明了不要在dispatch level下访问分页内存的原因:

     // Issue the read request.

        //

        status = IoPageRead (ReadBlock->FilePointer,

                             &ReadBlock->Mdl,

                             &ReadBlock->ReadOffset,

                             &ReadBlock->Event,

                             &ReadBlock->IoStatus);

        if (!NT_SUCCESS(status)) {

            //

            // Set the event as the I/O system doesn't set it on errors.

            //

            ReadBlock->IoStatus.Status = status;

            ReadBlock->IoStatus.Information = 0;

            KeSetEvent (&ReadBlock->Event, 0, FALSE);

        }

        //

        // Initializing PageFrameIndex is not needed for correctness, but

        // without it the compiler cannot compile this code W4 to check

        // for use of uninitialized variables.

        //

        PageFrameIndex = (PFN_NUMBER)-1;

        //

        // Wait for the I/O operation.

        //

        status = MiWaitForInPageComplete (ReadBlock->Pfn,

                                          ReadPte,

                                          VirtualAddress,

                                          &OriginalPte,

                                          CapturedEvent,

                                          Process);

注:CapturedEvent和ReadBlock是同一块内存来的,

ChildEBP RetAddr  

f7556358 8084bc13 nt!MiWaitForInPageComplete+0xd [c:\wrk-v1.2\base\ntos\mm\pagfault.c @ 4174]

f75563e0 80856fda nt!MiDispatchFault+0xd57 [c:\wrk-v1.2\base\ntos\mm\pagfault.c @ 1271]

f755643c 808858d8 nt!MmAccessFault+0x9c0 [c:\wrk-v1.2\base\ntos\mm\mmfault.c @ 2001]

f755643c 75de7c8b nt!_KiTrap0E+0xdc [C:\WRK-v1.2\base\ntos\ke\i386\trap.asm @ 5527]

0129f508 75db67b5 BROWSEUI!LoadToolbarBackBmp+0xf4

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券