内核里也需要访问用户应用程序内存,那么有什么方法呢?在ReactOS主要有两种方法:一种是使用缓冲I/O的方法,在驱动程序运行前,I/O管理器把写数据复制到这个缓冲区,并在请求完成时把读数据复制回到用户空间;另一种是使用直接I/O,这是优先的技术,因为它减少数据复制。这是通过I/O管理器传递一个内存描述符列表(MDL-- Memory descriptor list)来实现的,这个描述符列表是描述用户空间缓冲区。
MDL的实现代码如下:
#001 PMDL
#002 NTAPI
#003 IoAllocateMdl(IN PVOID VirtualAddress,
#004 IN ULONG Length,
#005 IN BOOLEAN SecondaryBuffer,
#006 IN BOOLEAN ChargeQuota,
#007 IN PIRP Irp)
#008 {函数IoAllocateMdl前两个参数定义了虚拟地址和内存区的大小,是建立MDL所必须的。如果MDL不与IRP相关联,则第三个参数就为FALSE。第四个参数定义是否需要减少进程的份额,并只用于位于驱动程序链最上层的驱动程序或是单层的驱动程序(。每一个进程都要获得一定份额的系统资源。当进程为自己分配资源时,这个份额就会减小。如果份额用完,就不能再为其分配相应的资源。最后一个参数定义了一个非必要的指向IRP的指针,通过这个指针MDL可以与IRP关联。例如,对于直接I/O,I/O管理器为用户缓冲区建立MDL,并将其地址送至IRP.MdlAddress。
#009 PMDL Mdl = NULL, p; #010 ULONG Flags = 0; #011 ULONG Size; #012
如果申请的内存超过2G,就返回失败。
#013 /* Fail if allocation is over 2GB */ #014 if (Length & 0x80000000) return NULL; #015
计算需要使用多少页内存。
#016 /* Calculate the number of pages for the allocation */
#017 Size = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress, Length);
#018 if (Size > 23)
#019 {大于23页,就计算实际使用的大小页。
#020 /* This is bigger then our fixed-size MDLs. Calculate real size */
#021 Size *= sizeof(PFN_NUMBER);
#022 Size += sizeof(MDL);
#023 if (Size > MAXUSHORT) return NULL;
#024 }
#025 else
#026 {如果小于等于23页,就直接使用23页的大小。
#027 /* Use an internal fixed MDL size */ #028 Size = (23 * sizeof(PFN_NUMBER)) + sizeof(MDL); #029 Flags |= MDL_ALLOCATED_FIXED_SIZE; #030
从后备列表里找到合适的内存。
#031 /* Allocate one from the lookaside list */ #032 Mdl = IopAllocateMdlFromLookaside(LookasideMdlList); #033 } #034
如果前面没有找到相应的MDL内存,就重新分配一个。
#035 /* Check if we don't have an mdl yet */
#036 if (!Mdl)
#037 {
#038 /* Allocate one from pool */
#039 Mdl = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_MDL);
#040 if (!Mdl) return NULL;
#041 }
#042 通过内存管理器的函数MmInitializeMdl初始化MDL。
#043 /* Initialize it */ #044 MmInitializeMdl(Mdl, VirtualAddress, Length); #045 Mdl->MdlFlags |= Flags; #046
检查IRP是否存在,如果存在就把MDL的地址放到IRP包里。
#047 /* Check if an IRP was given too */
#048 if (Irp)
#049 {
#050 /* Check if it came with a secondary buffer */
#051 if (SecondaryBuffer)
#052 {
#053 /* Insert the MDL at the end */
#054 p = Irp->MdlAddress;
#055 while (p->Next) p = p->Next;
#056 p->Next = Mdl;
#057 }
#058 else
#059 {
#060 /* Otherwise, insert it directly */
#061 Irp->MdlAddress = Mdl;
#062 }
#063 }
#064