首页 >> 编程开发 >> 其他 >> 正文

reactos操作系统实现(85)

  内核里也需要访问用户应用程序内存,那么有什么方法呢?在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