内核里也需要访问用户应用程序内存,那么有什么方法呢?在@H_502_5@ReactOS主要有两种方法:一种是使用缓冲@H_502_5@I/O的方法,在驱动程序运行前,@H_502_5@I/O管理器把写数据复制到这个缓冲区,并在请求完成时把读数据复制回到用户空间;另一种是使用直接@H_502_5@I/O,这是优先的技术,因为它减少数据复制。这是通过@H_502_5@I/O管理器传递一个内存描述符列表(@H_502_5@MDL--@H_502_5@ @H_502_5@Memory descriptor list)来实现的,这个描述符列表是描述用户空间缓冲区。@H_502_5@
@H_502_5@#001 PMDL
@H_502_5@#002 NTAPI
@H_502_5@#003 IoAllocateMdl(IN PVOID VirtualAddress,
@H_502_5@#004 IN ULONG Length,
@H_502_5@#005 IN BOOLEAN SecondaryBuffer,
@H_502_5@#006 IN BOOLEAN ChargeQuota,
@H_502_5@#007 IN PIRP Irp)
@H_502_5@#008 {
函数@H_502_5@IoAllocateMdl前两个参数定义了虚拟地址和内存区的大小,是建立@H_502_5@MDL所必须的。如果@H_502_5@MDL不与@H_502_5@IRP相关联,则第三个参数就为@H_502_5@FALSE。第四个参数定义是否需要减少进程的份额,并只用于位于驱动程序链最上层的驱动程序或是单层的驱动程序(。每一个进程都要获得一定份额的系统资源。当进程为自己分配资源时,这个份额就会减小。如果份额用完,就不能再为其分配相应的资源。最后一个参数定义了一个非必要的指向@H_502_5@IRP的指针,通过这个指针@H_502_5@MDL可以与@H_502_5@IRP关联。例如,对于直接@H_502_5@I/O,@H_502_5@I/O管理器为用户缓冲区建立@H_502_5@MDL,并将其地址送至@H_502_5@IRP.MdlAddress。@H_502_5@
@H_502_5@
@H_502_5@#009 PMDL Mdl = NULL,p;
@H_502_5@#010 ULONG Flags = 0;
@H_502_5@#011 ULONG Size;
@H_502_5@#012
@H_502_5@
如果申请的内存超过@H_502_5@2G,就返回失败。@H_502_5@
@H_502_5@#013 /* Fail if allocation is over 2GB */
@H_502_5@#014 if (Length & 0x80000000) return NULL;
@H_502_5@#015
@H_502_5@
计算需要使用多少页内存。@H_502_5@
@H_502_5@#016 /* Calculate the number of pages for the allocation */
@H_502_5@#017 Size = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress,Length);
@H_502_5@#018 if (Size > 23)
@H_502_5@#019 {
@H_502_5@
大于@H_502_5@23页,就计算实际使用的大小页。@H_502_5@
@H_502_5@#020 /* This is bigger then our fixed-size MDLs. Calculate real size */
@H_502_5@#021 Size *= sizeof(PFN_NUMBER);
@H_502_5@#022 Size += sizeof(MDL);
@H_502_5@#023 if (Size > MAXUSHORT) return NULL;
@H_502_5@#024 }
@H_502_5@#025 else
@H_502_5@#026 {
如果小于等于@H_502_5@23页,就直接使用@H_502_5@23页的大小。@H_502_5@
@H_502_5@#027 /* Use an internal fixed MDL size */
@H_502_5@#028 Size = (23 * sizeof(PFN_NUMBER)) + sizeof(MDL);
@H_502_5@#029 Flags |= MDL_ALLOCATED_FIXED_SIZE;
@H_502_5@#030
@H_502_5@
从后备列表里找到合适的内存。@H_502_5@
@H_502_5@#031 /* Allocate one from the lookaside list */
@H_502_5@#032 Mdl = IopAllocateMdlFromLookaside(LookasideMdlList);
@H_502_5@#033 }
@H_502_5@#034
@H_502_5@
如果前面没有找到相应的@H_502_5@MDL内存,就重新分配一个。@H_502_5@
@H_502_5@#035 /* Check if we don't have an mdl yet */
@H_502_5@#036 if (!Mdl)
@H_502_5@#037 {
@H_502_5@#038 /* Allocate one from pool */
@H_502_5@#039 Mdl = ExAllocatePoolWithTag(NonPagedPool,Size,TAG_MDL);
@H_502_5@#040 if (!Mdl) return NULL;
@H_502_5@#041 }
@H_502_5@#042
@H_502_5@
通过内存管理器的函数@H_502_5@MmInitializeMdl初始化@H_502_5@MDL。@H_502_5@
@H_502_5@#043 /* Initialize it */
@H_502_5@#044 MmInitializeMdl(Mdl,VirtualAddress,Length);
@H_502_5@#045 Mdl->MdlFlags |= Flags;
@H_502_5@#046
@H_502_5@
检查@H_502_5@IRP是否存在,如果存在就把@H_502_5@MDL的地址放到@H_502_5@IRP包里。@H_502_5@
@H_502_5@#047 /* Check if an IRP was given too */
@H_502_5@#048 if (Irp)
@H_502_5@#049 {
@H_502_5@#050 /* Check if it came with a secondary buffer */
@H_502_5@#051 if (SecondaryBuffer)
@H_502_5@#052 {
@H_502_5@#053 /* Insert the MDL at the end */
@H_502_5@#054 p = Irp->MdlAddress;
@H_502_5@#055 while (p->Next) p = p->Next;
@H_502_5@#056 p->Next = Mdl;
@H_502_5@#057 }
@H_502_5@#058 else
@H_502_5@#059 {
@H_502_5@#060 /* Otherwise,insert it directly */
@H_502_5@#061 Irp->MdlAddress = Mdl;
@H_502_5@#062 }
@H_502_5@#063 }
@H_502_5@#064
@H_502_5@
@H_502_5@#065 /* Return the allocated mdl */
@H_502_5@#066 return Mdl;
@H_502_5@#067 }
@H_502_5@
@H_502_5@
在@H_502_5@MDL描述符表里,还有这样的需求,当作一个@H_502_5@MDL表已经映射过一次@H_502_5@MDL了,那么当用户再次想去分配这个@H_502_5@MDL时,就需要使用函数@H_502_5@IoBuildPartialMdl来再次映射@H_502_5@MDL了。其实出现这种情况,就是当驱动程序使用了一个@H_502_5@MDL的@H_502_5@IRP包发送给另外一个驱动程序,然后这个驱动程序又需要从@H_502_5@IRP包里的@H_502_5@MDL再分配一个@H_502_5@MDL出来。这个函数的实现代码如下:@H_502_5@
@H_502_5@#001 /*
@H_502_5@#002 * @implemented
@H_502_5@#003 */
@H_502_5@#004 VOID
@H_502_5@#005 NTAPI
@H_502_5@#006 IoBuildPartialMdl(IN PMDL SourceMdl,
@H_502_5@#007 IN PMDL TargetMdl,
@H_502_5@#008 IN PVOID VirtualAddress,
@H_502_5@#009 IN ULONG Length)
@H_502_5@#010 {
@H_502_5@
@H_502_5@#011 PPFN_NUMBER TargetPages = (PPFN_NUMBER)(TargetMdl + 1);
@H_502_5@
@H_502_5@#012 PPFN_NUMBER SourcePages = (PPFN_NUMBER)(SourceMdl + 1);
@H_502_5@#013 ULONG Offset;
@H_502_5@#014 ULONG FlagsMask = (MDL_IO_PAGE_READ |
@H_502_5@#015 MDL_SOURCE_IS_NONPAGED_POOL |
@H_502_5@#016 MDL_MAPPED_TO_SYSTEM_VA |
@H_502_5@#017 MDL_IO_SPACE);
@H_502_5@#018
@H_502_5@
计算偏移位置。@H_502_5@
@H_502_5@#019 /* Calculate the offset */
@H_502_5@#020 Offset = (ULONG)((ULONG_PTR)VirtualAddress -
@H_502_5@#021 (ULONG_PTR)SourceMdl->StartVa) -
@H_502_5@#022 SourceMdl->ByteOffset;
@H_502_5@#023
@H_502_5@
计算源@H_502_5@MDL是否有足够的长度。@H_502_5@
@H_502_5@#024 /* Check if we don't have a length and calculate it */
@H_502_5@#025 if (!Length) Length = SourceMdl->ByteCount - Offset;
@H_502_5@#026
@H_502_5@
设置进程、虚拟地址和需要内存的大小。@H_502_5@
@H_502_5@#027 /* Write the process,start VA and byte data */
@H_502_5@#028 TargetMdl->StartVa = (PVOID)PAGE_ROUND_DOWN(VirtualAddress);
@H_502_5@#029 TargetMdl->Process = SourceMdl->Process;
@H_502_5@#030 TargetMdl->ByteCount = Length;
@H_502_5@#031 TargetMdl->ByteOffset = BYTE_OFFSET(VirtualAddress);
@H_502_5@#032
@H_502_5@
@H_502_5@#033 /* Recalculate the length in pages */
@H_502_5@#034 Length = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress,Length);
@H_502_5@#035
@H_502_5@
@H_502_5@#036 /* Set the MDL Flags */
@H_502_5@#037 TargetMdl->MdlFlags &= (MDL_ALLOCATED_FIXED_SIZE | MDL_ALLOCATED_MUST_SUCCEED);
@H_502_5@#038 TargetMdl->MdlFlags |= SourceMdl->MdlFlags & FlagsMask;
@H_502_5@#039 TargetMdl->MdlFlags |= MDL_PARTIAL;
@H_502_5@#040
@H_502_5@#041 /* Set the mapped VA */
@H_502_5@#042 TargetMdl->MappedSystemVa = (PCHAR)SourceMdl->MappedSystemVa + Offset;
@H_502_5@#043
@H_502_5@
开始从源@H_502_5@MDL里拷贝数据到新的@H_502_5@MDL。@H_502_5@
@H_502_5@#044 /* Now do the copy */
@H_502_5@#045 Offset = ((ULONG_PTR)TargetMdl->StartVa - (ULONG_PTR)SourceMdl->StartVa) >>
@H_502_5@#046 PAGE_SHIFT;
@H_502_5@#047 SourcePages += Offset;
@H_502_5@#048 RtlCopyMemory(TargetPages,SourcePages,Length * sizeof(PFN_NUMBER));
@H_502_5@#049 }
@H_502_5@
前面都是创建@H_502_5@MDL的,下面这个函数就是删除@H_502_5@MDL所占用的资源,实现如下:@H_502_5@
@H_502_5@#001 VOID
@H_502_5@#002 NTAPI
@H_502_5@#003 IoFreeMdl(PMDL Mdl)
@H_502_5@#004 {
@H_502_5@
让内存管理器删除所有@H_502_5@MDL。@H_502_5@
@H_502_5@#005 /* Tell Mm to reuse the MDL */
@H_502_5@#006 MmPrepareMdlForReuse(Mdl);
@H_502_5@#007
@H_502_5@
检查是否重新分配的内存,如果是就需要删除掉,否则就放回到后备列表,以便下一次使用。@H_502_5@
@H_502_5@#008 /* Check if this was a pool allocation */
@H_502_5@#009 if (!(Mdl->MdlFlags & MDL_ALLOCATED_FIXED_SIZE))
@H_502_5@#010 {
@H_502_5@#011 /* Free it from the pool */
@H_502_5@#012 ExFreePoolWithTag(Mdl,TAG_MDL);
@H_502_5@#013 }
@H_502_5@#014 else
@H_502_5@#015 {
@H_502_5@#016 /* Free it from the lookaside */
@H_502_5@#017 IopFreeMdlFromLookaside(Mdl,LookasideMdlList);
@H_502_5@#018 }
@H_502_5@#019 }
@H_502_5@#020
原文链接:https://www.f2er.com/react/308443.html