MmProbeAndLockPages

MmProbeAndLockPages 到底鎖定什麼?

驅動程序有時候需要鎖定內存頁以使得它們在特定的操作期間駐留在內存中,例如在 DPC 例程中將數據從設備複製到數據緩衝區或者對緩衝區進行 DMA。MmProbeAndLockPages 例程使得一個指定的內存範圍駐留(如果尚未駐留),確認內存頁允許以指定的訪問模式執行指定的操作,並鎖定內存中的頁以使得它們不能被頁交換出去。

MmProbeAndLockPages 通過在頁幀號 (page frame number) 數據庫(描述物理內存中每個頁的狀態)中的頁幀號(PFN)條目上增加引用計數實現這個功能。只要 PFN 引用計數非零,物理頁就不會被重用。

但是,不要混淆物理頁及其虛擬地址,這一點非常重要。MmProbeAndLockPages 在內存中鎖定物理頁,但是如果相關的工作集被修改(或者如果進程釋放或取消映射地址範圍),那麼它們的虛擬地址會變得無效。即使可以保證後備物理頁保持駐留,虛擬地址映射也沒有保證。驅動程序中這種情況的一個症狀是 MmIsAddressValid 將指示虛擬地址頁故障,認爲內存已經被頁交換出去。如果驅動程序試圖使用虛擬地址(假設地址範圍未被刪除),那麼它會遇到與試圖使用可分頁內存時相同的問題:如果驅動程序以低於 DISPATCH_LEVEL 的級別運行,那麼系統可以指示頁故障;如果驅動程序在以 DISPATCH_LEVEL 或高於 DISPATCH_LEVEL 的級別運行,那麼系統將進行錯誤檢查。(關於 Windows 如何管理虛擬內存和物理內存的深入討論,請參見 Inside Windows 2000(第三版)的第 7 章。)

如果在 DISPATCH_LEVEL 或高於 DISPATCH_LEVEL 的級別上需要虛擬地址,或者在另一個進程上下文中需要虛擬地址,那麼驅動程序必須使用 MmGetSystemAddressForMdlSafe 映射 MDL 來獲取 MDL 的未經修改的系統虛擬地址。這個虛擬地址從未被插入到工作集中,因此它不能被刪除。不管線程或 IRQL 的進程上下文(在其中引用地址)如何,它都能保證正常工作。

您應該做什麼?

  • 調用 MmProbeAndLockPages 在內存中鎖定 MDL 描述的頁。始終應該在 try/except 塊中執行此操作,因爲如果 MmProbeAndLockPages 失敗,將拋出一個異常。

  • 要將鎖定的物理頁映射到系統空間,請使用 MDL 的指針來調用 MmGetSystemAddressForMdlSafe。(爲必須在 Windows 98 上運行的驅動程序使用 MmGetSystemAddressForMdl。)爲了避免浪費系統資源,只有在確實需要使用虛擬地址來訪問內存頁時才進行這種操作。

  • 使用 MmGetSystemAddressForMdlSafe 返回的系統地址來通過虛擬地址訪問鎖定的頁。

  • 當您不再需要 MDL 描述的頁時,請調用 MmUnlockPages 將它們解除鎖定,然後調用 IoFreeMdl 來釋放它們。

下列來自 Windows DDK(%winddk%/src/general/ioctl/sys/sioctl.c)中的系統 IOCTL 例子代碼片段展示瞭如何進行這種操作:

mdl = IoAllocateMdl(inBuf, inBufLength,  FALSE, TRUE, NULL);        if(!mdl)        {            ntStatus = STATUS_INSUFFICIENT_RESOURCES;            break;        }                    try         {                                   //            // Probe and lock the pages of this buffer in physical memory.            // You can specify IoReadAccess, IoWriteAccess or IoModifyAccess            // Always perform this operation in a try except block.             //  MmProbeAndLockPages will raise an exception if it fails.            //            MmProbeAndLockPages(mdl, UserMode, IoReadAccess);        }        except(EXCEPTION_EXECUTE_HANDLER)         {             ntStatus = GetExceptionCode();            SIOCTL_KDPRINT((                "Exception while locking inBuf 0X%08X in METHOD_NEITHER/n",                    ntStatus));            IoFreeMdl(mdl);            break;        }         //                 // Map the physical pages described by the MDL into system space.         // Note: double mapping the buffer this way causes lot of         // system overhead for large size buffers.         //          buffer = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority );         if(!buffer) {                ntStatus = STATUS_INSUFFICIENT_RESOURCES;                MmUnlockPages(mdl);                IoFreeMdl(mdl);                break;                   }                //         // Now you can safely read the data from the buffer.        //        SIOCTL_KDPRINT(("/tData from User (SystemAddress) : "));        PrintChars(buffer, inBufLength);             //        // Once the read is over unmap and unlock the pages.        //         MmUnlockPages(mdl);        IoFreeMdl(mdl);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章