首页
社区
课程
招聘
[讨论]WRK1.2中断处理中MiResolveMappedFileFault函数试读
发表于: 2009-3-13 10:39 4750

[讨论]WRK1.2中断处理中MiResolveMappedFileFault函数试读

2009-3-13 10:39
4750
/*
菜鸟路过~~
额是菜鸟,大家预备好板砖,狠狠砸~~牛人看过此文,切记莫笑掉大牙~
发此贴主要是同诸位交流,还有很多不甚明白的地方,已标注,还望大家不吝赐教!
读了MiResolveMappedFileFault函数,是页面中断出错时调用的一个子程序,
参数PointerPte是出错的PTE,注意这是一个原型PTE
MiResolveMappedFileFault函数的主要作用是根据Process和Controlarea以及系统情况来得到
应从文件的何处读,并且设好PFN。参考资料是解析Winndows 2000/XP物理内存管理等文
*/
NTSTATUS
MiResolveMappedFileFault (
    IN PMMPTE PointerPte,
    OUT PMMINPAGE_SUPPORT *ReadBlock,
    IN PEPROCESS Process,
    IN KIRQL OldIrql
    )

/*++

Routine Description:

    This routine builds the MDL and other structures to allow a
    read operation on a mapped file for a page fault.

Arguments:

    PointerPte - Supplies the PTE for the faulting address.

    ReadBlock - Supplies a pointer to put the address of the read block which
                needs to be completed before an I/O can be issued.

    Process - Supplies a pointer to the process object.  If this
              parameter is NULL, then the fault is for system
              space and the process's working set lock is not held.

    OldIrql - Supplies the IRQL the caller acquired the PFN lock at.

Return Value:

    status.  A status value of STATUS_ISSUE_PAGING_IO is returned
    if this function completes successfully.

Environment:

    Kernel mode, PFN lock held.

--*/

{
    PFN_NUMBER PageFrameIndex;
    PMMPFN Pfn1;
    PSUBSECTION Subsection;
    PCONTROL_AREA ControlArea;
    PMDL Mdl;
    ULONG ReadSize;
    PETHREAD CurrentThread;
    PPFN_NUMBER Page;
    PPFN_NUMBER EndPage;
    PMMPTE BasePte;
    PMMPTE CheckPte;
    LARGE_INTEGER StartingOffset;
    LARGE_INTEGER TempOffset;
    PPFN_NUMBER FirstMdlPage;
    PMMINPAGE_SUPPORT ReadBlockLocal;
    ULONG PageColor;
    ULONG ClusterSize;
    PFN_NUMBER AvailablePages;
    NTSTATUS Status;
    PKPRCB Prcb;

    ClusterSize = 0;

    ASSERT (PointerPte->u.Soft.Prototype == 1);

    // *********************************************
    //   Mapped File (subsection format)
    // *********************************************

    if ((MmAvailablePages < MM_HIGH_LIMIT) &&
        (MiEnsureAvailablePageOrWait (Process, OldIrql))) {

        //
        // A wait operation was performed which dropped the locks,
        // repeat this fault.
        //

        return STATUS_REFAULT;
    }

    //
    // Calculate address of subsection for this prototype PTE.
    //这是个原型PTE。由此可以得到相应的subsection
    //MiGetSubsectionAddress是一个宏。在mi.h中

    Subsection = MiGetSubsectionAddress (PointerPte);

    ControlArea = Subsection->ControlArea;
                //该区不可更改
    if (ControlArea->u.Flags.FailAllIo) {
        return STATUS_IN_PAGE_ERROR;
    }
                //
                //这里可以进行判断,PointerPte是否是超出Subsection的范围
                //Subsection->SubsectionBase是该Subsection的原型PTE表的首地址,
                //&Subsection->SubsectionBase[Subsection->PtesInSubsection]为末地址
                //数组飘过~~
    if (PointerPte >= &Subsection->SubsectionBase[Subsection->PtesInSubsection]) {
                               
        //
        // Attempt to read past the end of this subsection.
        //返回,访问违例

        return STATUS_ACCESS_VIOLATION;
    }

    ASSERT (ControlArea->u.Flags.Rom != 1);
                //
                //ReadBlockLocal是指向MMINPAGE_SUPPORT结构的。MI.H中有其定义。
                //用来告诉调用者将来从映射文件中的何处偏移开始读,读多少,存入
                //到哪里等等
    CurrentThread = PsGetCurrentThread ();

    ReadBlockLocal = MiGetInPageSupportBlock (OldIrql, &Status);

    if (ReadBlockLocal == NULL) {
        ASSERT (!NT_SUCCESS (Status));
        return Status;
    }

    *ReadBlock = ReadBlockLocal;

    //
    // Build an MDL for the request.
    //

    Mdl = &ReadBlockLocal->Mdl;
                //PFN_NUMBER Page[MM_MAXIMUM_READ_CLUSTER_SIZE + 1]
                //实际上就是该数组的指针
    FirstMdlPage = &ReadBlockLocal->Page[0];
    //
    //page在下面很重要
    Page = FirstMdlPage;

#if DBG
    RtlFillMemoryUlong (Page, (MM_MAXIMUM_READ_CLUSTER_SIZE+1) * sizeof(PFN_NUMBER), 0xf1f1f1f1);
#endif //DBG
                //
                //请诸位想PointerPte是原型PTE页面表中的一项。
                //
    ReadSize = PAGE_SIZE;
    BasePte = PointerPte;

    //
    // Should we attempt to perform page fault clustering?
    //

    AvailablePages = MmAvailablePages;
                //额 ~~~汗,这里没看懂,抛砖引玉
    if (MiInPageSinglePages != 0) {
        AvailablePages = 0;
        MiInPageSinglePages -= 1;
    }
                //如果当前线程允许预读(我是这样理解的,应该是)当前线程可以预读页面,
                //同时当前控制区可以修改
    if ((!CurrentThread->DisablePageFaultClustering) &&
        (ControlArea->u.Flags.NoModifiedWriting == 0)) {

        if ((AvailablePages > (MmFreeGoal * 2))
                 ||
         (((ControlArea->u.Flags.Image != 0) ||
            (CurrentThread->ForwardClusterOnly)) &&
         (AvailablePages > MM_HIGH_LIMIT))) {

            //
            // Cluster up to n pages.  This one + n-1.
            //

            ASSERT (MM_HIGH_LIMIT > MM_MAXIMUM_READ_CLUSTER_SIZE + 16);
            ASSERT (AvailablePages > MM_MAXIMUM_READ_CLUSTER_SIZE + 16);
                                                //
                                                //在这种情况下,可以cluster read,那么就要根据control area
                                                //是Image类型的还是Data类型的,设置ClusterSize.
                                                //什么样子的类型,通过file_object->_SECTION_OBJECT_POINTERS
                                                //该项查看
            if (ControlArea->u.Flags.Image == 0) {
                ASSERT (CurrentThread->ReadClusterSize <=
                            MM_MAXIMUM_READ_CLUSTER_SIZE);
                ClusterSize = CurrentThread->ReadClusterSize;
            }
            else {
                ClusterSize = MmDataClusterSize;
                if (Subsection->u.SubsectionFlags.Protection &
                                            MM_PROTECTION_EXECUTE_MASK ) {
                    ClusterSize = MmCodeClusterSize;
                }
            }
            //以上几句是根据控制区和线程的设置来得到ClusterSize的大小
            //
            //ReadBlockLocal下的那个PFN_NUMBER Page[MM_MAXIMUM_READ_CLUSTER_SIZE + 1]
            //EndPage指向第ClusterSize元素
            EndPage = Page + ClusterSize;
                                                //
                                                //
                                                //指向下一个的PTE
            CheckPte = PointerPte + 1;

            //
            // Try to cluster within the page of PTEs.
            //在PTE页面中集中使用。
            //指的是说,CheckPTE在PTE页面中
            //#define MiIsPteOnPdeBoundary(PTE) (((ULONG_PTR)(PTE) & (PAGE_SIZE - 1)) == 0)
                                                //Page < EndPage根据ClusterSize设置
                                                //CheckPte <
                                                //                 &Subsection->SubsectionBase[Subsection->PtesInSubsection]
            //是CheckPte莫要超过subsection的区间
            //再下一个,额猜是页面属性相同
            while ((MiIsPteOnPdeBoundary(CheckPte) == 0) &&
               (Page < EndPage) &&
               (CheckPte <
                 &Subsection->SubsectionBase[Subsection->PtesInSubsection])
                      && (CheckPte->u.Long == BasePte->u.Long)) {

                ControlArea->NumberOfPfnReferences += 1;
                ReadSize += PAGE_SIZE;
                Page += 1;
                CheckPte += 1;
            }
            
            //CheckPte起到游标的作用
            //看看当前线程允许往前集中么
            if ((Page < EndPage) && (!CurrentThread->ForwardClusterOnly)) {

                //
                // Attempt to cluster going backwards from the PTE.
                //

                CheckPte = PointerPte - 1;
                                                                //第一个判断是什么意思?
                while ((((ULONG_PTR)CheckPte & (PAGE_SIZE - 1)) !=
                                            (PAGE_SIZE - sizeof(MMPTE))) &&
                        (Page < EndPage) &&
                         (CheckPte >= Subsection->SubsectionBase) &&
                         (CheckPte->u.Long == BasePte->u.Long)) {

                    ControlArea->NumberOfPfnReferences += 1;
                    ReadSize += PAGE_SIZE;
                    Page += 1;
                    CheckPte -= 1;
                }
                BasePte = CheckPte + 1;
                //BasePte得到的是Subsection可以从映射文件读的第一个PTE。
                //
            }
        }
    }

    //
    //
    // Calculate the offset to read into the file.
    //  offset = base + ((thispte - basepte) << PAGE_SHIFT)
    //base是该Subsection得到的分区的及地址,然后加上原来的
    //Subsection的偏移。MiStartingOffset很重要。见文末。
   

    StartingOffset.QuadPart = MiStartingOffset (Subsection, BasePte);

    TempOffset = MiEndingOffset (Subsection);

    ASSERT (StartingOffset.QuadPart < TempOffset.QuadPart);

    //
    // Remove pages to fill in the MDL.  This is done here as the
    // base PTE has been determined and can be used for virtual
    // aliasing(混淆现象) checks.
    //Page是Subsection页面的终结,BasePte是最底层的那个PTE

    EndPage = FirstMdlPage;
    CheckPte = BasePte;
                //
                //卖糕的,额不知道下面是什么意思,只知道取得页帧,
                //将其给page,就是那个老是提到的数组。
                //什么是PageColor?
    while (EndPage < Page) {
        if (Process == HYDRA_PROCESS) {
            PageColor = MI_GET_PAGE_COLOR_FROM_SESSION (MmSessionSpace);
        }
        else if (Process == NULL) {
            PageColor = MI_GET_PAGE_COLOR_FROM_PTE (CheckPte);
        }
        else {
            PageColor = MI_PAGE_COLOR_PTE_PROCESS (CheckPte,
                                                   &Process->NextPageColor);
        }
        *EndPage = MiRemoveAnyPage (PageColor);

        EndPage += 1;
        CheckPte += 1;
    }

    if (Process == HYDRA_PROCESS) {
        PageColor = MI_GET_PAGE_COLOR_FROM_SESSION (MmSessionSpace);
    }
    else if (Process == NULL) {
        PageColor = MI_GET_PAGE_COLOR_FROM_PTE (CheckPte);
    }
    else {
        PageColor = MI_PAGE_COLOR_PTE_PROCESS (CheckPte,
                                               &Process->NextPageColor);
    }

    //
    // Check to see if the read will go past the end of the file,
    // If so, correct the read size and get a zeroed page.
    //

    Prcb = KeGetCurrentPrcb ();
    InterlockedIncrement (&Prcb->MmPageReadIoCount);

    InterlockedExchangeAdd (&Prcb->MmPageReadCount,
                            (LONG) (ReadSize >> PAGE_SHIFT));
                //如果控制区是可执行文件的映像,并且超过文件的大小,那么重新设定读的大小
                //从这里来看, 貌似是一个subsection对应一个文件对象
                //
    if ((ControlArea->u.Flags.Image) &&
        (((UINT64)StartingOffset.QuadPart + ReadSize) > (UINT64)TempOffset.QuadPart)) {

        ASSERT ((ULONG)(TempOffset.QuadPart - StartingOffset.QuadPart)
                > (ReadSize - PAGE_SIZE));

        ReadSize = (ULONG)(TempOffset.QuadPart - StartingOffset.QuadPart);

        //
        // Round the offset to a 512-byte offset as this will help filesystems
        // optimize the transfer.  Note that filesystems will always zero fill
        // the remainder between VDL and the next 512-byte multiple and we have
        // already zeroed the whole page.
        //

        ReadSize = ((ReadSize + MMSECTOR_MASK) & ~MMSECTOR_MASK);

        PageFrameIndex = MiRemoveZeroPage (PageColor);
    }
    else {

        //
        // We are reading a complete page, no need to get a zeroed page.
        //

        PageFrameIndex = MiRemoveAnyPage (PageColor);
    }

    //
    // Increment the PFN reference count in the control area for
    // the subsection (the PFN lock is required to modify this field).
    //

    ControlArea->NumberOfPfnReferences += 1;
    //现在才赋值page,
    //page最早是赋值FirstMdlPage的,后来根据Clustersize和其他来不断增加。
    *Page = PageFrameIndex;
                //
                //这里FirstMdlPage还是那个数组的起始地址,PointerPte是
                //出错的PTE地址,BasePte是PointerPte下面的那个PTE
                //由代码来看的话,应当是ReadBlockLocal的PFN_NUMBER Page[MM_MAXIMUM_READ_CLUSTER_SIZE + 1]
                //元素应该已经有了PageFrameIndex了。不知这样理解正确否?
    PageFrameIndex = *(FirstMdlPage + (PointerPte - BasePte));

    //
    // Get a page and put the PTE into the transition state with the
    // read-in-progress flag set.
    //

    Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);

    //
    // Initialize MDL for request.
    //初始化MDL

    MmInitializeMdl (Mdl,
                     MiGetVirtualAddressMappedByPte (BasePte),
                     ReadSize);
    Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);

#if DBG
    if (ReadSize > ((ClusterSize + 1) << PAGE_SHIFT)) {
        KeBugCheckEx (MEMORY_MANAGEMENT, 0x777,(ULONG_PTR)Mdl, (ULONG_PTR)Subsection,
                        (ULONG)TempOffset.LowPart);
    }
#endif //DBG
                //这个没看,应当是设置读时的,PFN设置问题,防止他用
    MiInitializeReadInProgressPfn (Mdl,
                                   BasePte,
                                   &ReadBlockLocal->Event,
                                   MI_PROTOTYPE_WSINDEX);

    MI_ZERO_USED_PAGETABLE_ENTRIES_IN_INPAGE_SUPPORT(ReadBlockLocal);
               
    ReadBlockLocal->ReadOffset = StartingOffset;
    ReadBlockLocal->FilePointer = ControlArea->FilePointer;
    ReadBlockLocal->BasePte = BasePte;
    ReadBlockLocal->Pfn = Pfn1;

    return STATUS_ISSUE_PAGING_IO;
}

LONGLONG
FORCEINLINE
MiStartingOffset (
    IN PSUBSECTION Subsection,
    IN PMMPTE PteAddress
    )

/*++

Routine Description:

    This function calculates the file offset given a subsection and a PTE
    offset.  Note that images are stored in 512-byte units whereas data is
    stored in 4K units.

Arguments:

    Subsection - Supplies a subsection to reference for the file address.

    PteAddress - Supplies a PTE within the subsection

Return Value:

    Returns the file offset to obtain the backing data from.

MiStartingOffset (Subsection, BasePte);这是在ResolveMappedFile中的调用
--*/

{
    LONGLONG PteByteOffset;
    LARGE_INTEGER StartAddress;
                //如果该subsection指向的是可执行文件映像
                //
    if (Subsection->ControlArea->u.Flags.Image == 1) {
        return MI_STARTING_OFFSET (Subsection, PteAddress);
    }
               
    ASSERT (Subsection->SubsectionBase != NULL);
                //得到要读入的字节大小
    PteByteOffset = (LONGLONG)((PteAddress - Subsection->SubsectionBase))
                            << PAGE_SHIFT;
                /*
                #define Mi4KStartFromSubsection(address, subsection)  \
   ((PLARGE_INTEGER)address)->LowPart = subsection->StartingSector; \
   ((PLARGE_INTEGER)address)->HighPart = subsection->u.SubsectionFlags.StartingSector4132;
                MM4K_SHIFT 12
                */
                //Mi4KStartFromSubsection得到的是该subsection在
                //映射文件中的偏移,但是subsection->StartingSector
                //让我产生的疑惑是难道用到的是扇区?
                //但是从紧接着的一句来看,又貌似是文件中的绝对位移~
                //faint,哪位大侠帮帮忙?
    Mi4KStartFromSubsection (&StartAddress, Subsection);

    StartAddress.QuadPart = StartAddress.QuadPart << MM4K_SHIFT;
                //可以一次性读出的大小+文件已有的偏移
    PteByteOffset += StartAddress.QuadPart;

    return PteByteOffset;
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 364
活跃值: (152)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
2
沙发~~支持一下!
似乎再写个总结性的论述更  好些
2009-3-13 12:47
0
游客
登录 | 注册 方可回帖
返回
//