加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

reactos操作系统实现(85)

发布时间:2020-12-15 05:00:44 所属栏目:百科 来源:网络整理
导读:内核里也需要访问用户应用程序内存,那么有什么方法呢?在 ReactOS 主要有两种方法:一种是使用缓冲 I/O 的方法,在驱动程序运行前, I/O 管理器把写数据复制到这个缓冲区,并在请求完成时把读数据复制回到用户空间;另一种是使用直接 I/O ,这是优先的技术

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

最后返回MDL的地址。

#065 /* Return the allocated mdl */

#066 return Mdl;

#067 }

MDL描述符表里,还有这样的需求,当作一个MDL表已经映射过一次MDL了,那么当用户再次想去分配这个MDL时,就需要使用函数IoBuildPartialMdl来再次映射MDL了。其实出现这种情况,就是当驱动程序使用了一个MDLIRP包发送给另外一个驱动程序,然后这个驱动程序又需要从IRP包里的MDL再分配一个MDL出来。这个函数的实现代码如下:

#001 /*

#002 * @implemented

#003 */

#004 VOID

#005 NTAPI

#006 IoBuildPartialMdl(IN PMDL SourceMdl,

#007 IN PMDL TargetMdl,

#008 IN PVOID VirtualAddress,

#009 IN ULONG Length)

#010 {

取目标MDL地址。

#011 PPFN_NUMBER TargetPages = (PPFN_NUMBER)(TargetMdl + 1);

取源MDL地址。

#012 PPFN_NUMBER SourcePages = (PPFN_NUMBER)(SourceMdl + 1);

#013 ULONG Offset;

#014 ULONG FlagsMask = (MDL_IO_PAGE_READ |

#015 MDL_SOURCE_IS_NONPAGED_POOL |

#016 MDL_MAPPED_TO_SYSTEM_VA |

#017 MDL_IO_SPACE);

#018

计算偏移位置。

#019 /* Calculate the offset */

#020 Offset = (ULONG)((ULONG_PTR)VirtualAddress -

#021 (ULONG_PTR)SourceMdl->StartVa) -

#022 SourceMdl->ByteOffset;

#023

计算源MDL是否有足够的长度。

#024 /* Check if we don't have a length and calculate it */

#025 if (!Length) Length = SourceMdl->ByteCount - Offset;

#026

设置进程、虚拟地址和需要内存的大小。

#027 /* Write the process,start VA and byte data */

#028 TargetMdl->StartVa = (PVOID)PAGE_ROUND_DOWN(VirtualAddress);

#029 TargetMdl->Process = SourceMdl->Process;

#030 TargetMdl->ByteCount = Length;

#031 TargetMdl->ByteOffset = BYTE_OFFSET(VirtualAddress);

#032

重新计算页面的空间。

#033 /* Recalculate the length in pages */

#034 Length = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress,Length);

#035

设置MDL的标志。

#036 /* Set the MDL Flags */

#037 TargetMdl->MdlFlags &= (MDL_ALLOCATED_FIXED_SIZE | MDL_ALLOCATED_MUST_SUCCEED);

#038 TargetMdl->MdlFlags |= SourceMdl->MdlFlags & FlagsMask;

#039 TargetMdl->MdlFlags |= MDL_PARTIAL;

#040

#041 /* Set the mapped VA */

#042 TargetMdl->MappedSystemVa = (PCHAR)SourceMdl->MappedSystemVa + Offset;

#043

开始从源MDL里拷贝数据到新的MDL

#044 /* Now do the copy */

#045 Offset = ((ULONG_PTR)TargetMdl->StartVa - (ULONG_PTR)SourceMdl->StartVa) >>

#046 PAGE_SHIFT;

#047 SourcePages += Offset;

#048 RtlCopyMemory(TargetPages,SourcePages,Length * sizeof(PFN_NUMBER));

#049 }

前面都是创建MDL的,下面这个函数就是删除MDL所占用的资源,实现如下:

#001 VOID

#002 NTAPI

#003 IoFreeMdl(PMDL Mdl)

#004 {

让内存管理器删除所有MDL

#005 /* Tell Mm to reuse the MDL */

#006 MmPrepareMdlForReuse(Mdl);

#007

检查是否重新分配的内存,如果是就需要删除掉,否则就放回到后备列表,以便下一次使用。

#008 /* Check if this was a pool allocation */

#009 if (!(Mdl->MdlFlags & MDL_ALLOCATED_FIXED_SIZE))

#010 {

#011 /* Free it from the pool */

#012 ExFreePoolWithTag(Mdl,TAG_MDL);

#013 }

#014 else

#015 {

#016 /* Free it from the lookaside */

#017 IopFreeMdlFromLookaside(Mdl,LookasideMdlList);

#018 }

#019 }

#020

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读