• IoAllocateMdl,MmProbeAndLockPages的用法


    转载地址:https://blog.csdn.net/wdykanq/article/details/7752909

    IoAllocateMdl,MmProbeAndLockPages的用法  

    第一,MDL的一个用法是提供驱动程序访问用户模式数据缓冲区的一种方式:直接I/O。也就是说通过MDL告诉驱动程序如何访问用户模式的数据缓冲区,这很好理解;
    第二,第二个用法是这样的,一些驱动程序在执行直接 I/O 来满足设备 I/O 控制请求时也使用 MDL,常用的方式如下:
    1,分配一个buf,可能是分页或者非分页的;
    2,调用IoAllocateMdl,指向这个buf;
    3,对于分页内存来说,调用MmProbeAndLockPages以及MmGetSystemAddressForMdlSafe来锁定内存页,以防止被page out出去;
      对于非分页内存而言,调用MmBuildMdlForNonPagedPool映射到物理内存上。

    对于第二个用法,对于分页内存可能还有意义,保证其不被page out,但对于非分页内存,我觉得除了DMA以外,别的都没有用处,既然已经是非分页内存了,直接使用就好了,为什么还要生成一个MDL去做映射?我一直都不是非常理解,有可能根本就是理解错误,请大家指教

    IoAllocateMdl

     IoAllocateMdl 函数分配足够映射一块缓存的MDL,给定缓存的起始地址和长度.

    PMDL
    IoAllocateMdl(
        __in_opt PVOID
    VirtualAddress,
        __in ULONG
    Length,
        __in BOOLEAN
    SecondaryBuffer,
        __in BOOLEAN
    ChargeQuota,
        __inout_opt PIRP
    Irp OPTIONAL
        );

    驱动可以使用IoAllocateMdl 函数实现大缓存分片的目的,通过一个独立的MDL来映射缓存的一小部分,或者映射驱动分配的内存.驱动会调用MmBuildMdlForNonPagedPool 来设置MDL的内存,使得MDL描述驱动分配的缓存处于不可置换的内存中。

     Length参数指明了MDL指向的缓存的大小。在Windows Server 2003, Windows XP, 和Windows 2000, 能够分配的最大缓存字节数为PAGE_SIZE * (65535 - sizeof(MDL)) / sizeof(ULONG_PTR)。在Windows Vista和Windows Server 2008, 最大缓存字节数是(2GB - PAGE_SIZE).在Windows 7和Windows Server 2008 R2中, 最大缓存字节数是(4GB - PAGE_SIZE).

       如果SecondaryBuffer参数是FALSE, 这个函数会更新Irp->MdlAddress ,让它指向一个新的MDL, 也就是说让它支持大缓存分片吧。 如果SecondaryBuffer为TRUE, 这个函数把MDL添加到Irp->MdlAddress指向的链之后。

    MDL(memory descriptor list)

    http://hi.baidu.com/reversefish/blog

    typedef struct _MDL {
    struct _MDL *Next;
    CSHORT Size;
    CSHORT MdlFlags;
    struct _EPROCESS *Process;
    PVOID MappedSystemVa;
    PVOID StartVa;
    ULONG ByteCount;
    ULONG ByteOffset;
    } MDL, *PMDL;

    MDL只是一个对物理内存的描述,但是因为系统跟Driver都是使用虚拟内存,所以MDL就是把虚拟内存『映射』到物理内存(from DDK)。这样讲是很模糊的,其实MDL的作用很简单:当Driver要存取某段内存位置时,确保MDL所描述的内存位置不会引起page fault。

    取得MDL的虚拟内存位置。 DDK特别讲明,Lower-Level Driver不可以直接把这个Address拿来使用,因为这有可能是user-space的内存位置。因此,Driver必须呼叫MmGetSystemAddressForMdlSafe()来取得并锁定这个Address所对应到的system-space的内存位置。

    如果Driver希望建立的MDL是映射到Driver自己配置的Non-Paged记忆体的话,Driver还得呼叫MmBuildMdlForNonPagedPool()。这是因为IoAllocateMdl()只有配置记忆体,但是并没有Build MDL

    临时为用户空间缓冲区增添一个系统空间映射,这使同一组物理页面有了两个虚拟地址区间,其一就是原来的用户空间虚拟地址区间,其二则是系统空间的虚拟地址区间。于是,就可以通过系统空间的虚拟地址访问用户空间缓冲区了,直到完成操作而返回用户空间时才撤销系统空间的映射。这种方法称为“直接”方法。直接方法对于很小的缓冲区是不划算的,因为临时映射的建立和撤销需要一定的开销,对于大一点的缓冲区才合适。

    1.用户态到内核态

    BaseAddr = OpenKernel32();        //映射kernel32的section到本进程的低2G空间
    if (!BaseAddr)
    {
    KdPrint(("DriverEntry--OpenKernel32 failure! "));
    return 0;
    }

    KdPrint(("BaseAddr: 0x%08x ",BaseAddr));

    //创建一个MDL
    pMDL = IoAllocateMdl(BaseAddr,0x11c000,FALSE,FALSE,NULL);
    if (!pMDL)
    {
    KdPrint(("pMDL == NULL "));
    return 0;
    }

    _try
    {
    MmProbeAndLockPages(pMDL,UserMode,IoReadAccess);

    }
    _except(EXCEPTION_EXECUTE_HANDLER)
    {
    KdPrint(("MmProbeAndLockPages exception "));
    }

    _try
    {
    pMapedAddr = MmMapLockedPagesSpecifyCache(pMDL,KernelMode,MmCached,NULL,FALSE,NormalPagePriority);
    if (!pMapedAddr)
    {
    KdPrint(("pMapedAdd == NULL "));
    return 0;
    }

    }
    _except(EXCEPTION_EXECUTE_HANDLER)
    {
    KdPrint(("MmMapLockedPagesSpecifyCache exception "));
    }

    2.内核态到用户态

    pShareMM_SYS=ExAllocatePool(NonPagedPool,1024);//size必须是page的整数倍
    RtlZeroMemory(pShareMM_SYS,1024);

    pShareMM_MDL=IoAllocateMdl(pShareMM_SYS,1024,FALSE,FALSE,NULL);
    MmBuildMdlForNonPagedPool(pShareMM_MDL);

    pShareMM_User=MmMapLockedPagesSpecifyCache(pShareMM_MDL,UserMode,MmCached,NULL,FALSE,NormalPagePriority);
    KdPrint(("pShareMM_SYS的地址为: 0x%p ",(PUCHAR)pShareMM_SYS));
    KdPrint(("pShareMM_User的地址为: 0x%p ",(PUCHAR)pShareMM_User));

  • 相关阅读:
    Linux 套接字编程
    Linux 网络(连接)相关参数作用
    Python WSGI
    Ubuntu Cloud Image in Openstack
    AWK
    MySQL--开发技巧(一)
    spring MVC--配置注解
    javascript-JQuery样式篇(一)
    JSP--常用标签
    spring MVC basic
  • 原文地址:https://www.cnblogs.com/kuangke/p/10901446.html
Copyright © 2020-2023  润新知