Windows驱动跑在核心态(Kernel mode),驱动的调用者跑在用户态。如何使用户态进程与核心态驱动共享内存呢 ?

我们知道32位Windows中,默认状态下虚拟空间有4G,前2G是每个进程私有的,也就是说在进程切换的时候会变化,后2G是操作系统的,所以是固定的。既然用户态进程和核心态驱动在同一个进程空间里,是不是只要直接传个内存地址过来,就可以访问了?理论上可以但实际上不行,因为用户态的进程在不断地切换,使驱动运行时没法保证前面的用户态进程是哪个,也就不确定前2G虚拟地址空间的映射情况,那么用户态进程传来的地址也许不是合法的。

比较常用的做法是通过MDL进行内存的重映射。简单地说就是将同一块物理内存同时映射到用户态空间和核心态空间。

具体来说,可以有两种做法:用户态进程分配空间,内核态去映射。另一种是内核态分配空间,用户态进程去映射。

前者伪码:

// assume uva is a virtual address in user space, uva_size is its size
MDL * mdl = IoAllocateMdl(uva, uva_size, FALSE, FALSE, NULL);
ASSERT(mdl);
__try {
MmProbeAndLockPages(mdl, UserMode, IoReadAccess);
} __except(EXCEPTION_EXECUTE_HANDLER) {
DbgPrint("error code = %d", GetExceptionCode);
}
PVOID kva = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority);
// use kva
// … MmUnlockPages(mdl);
IoFreeMdl(mdl);

*记得在driver unload之前把mdl unlock和free掉,否则会BSoD。

后者伪码:

PVOID kva = ExAllocatePoolWithTag(NonPagedPool, 1024, (ULONG)'PMET');
MDL * mdl = IoAllocateMdl(uva, uva_size, FALSE, FALSE, NULL);
ASSERT(mdl);
__try {
MmBuildMdlForNonPagedPool(mdl);
} __except(EXCEPTION_EXECUTE_HANDLER) {
DbgPrint("error code = %d", GetExceptionCode);
} PVOID uva = MmMapLockedPagesSpecifyCache(mdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority);

*如果kva是分配在nonpagedpool,那这些物理页本身就是被lock住的,因此用的是MmBuildMdlForNonPagedPool,如果是分配在paged pool里的用MmProbeAndLockPages。

除了这种最原始的方式,Windows还提供了两种称为DO_BUFFERED_IO 和DO_DIRECT_IO的方式,前者中系统自动将用户态空间内存拷贝到了到核心态空间(Associated-Irp.SystemBuffer),后者由系统自动生成MDL(Irp->MdlAddress)。其实这两种方法本质都是系统帮忙做了上面的部分流程,从而可以让程序员省了那些操作。

前面提到了一个关键数据结构MDL(memorydescriptor list ),系统用它来描述虚拟空间对应物理内存的layout。MDL分为两部分:固定长部分和变长部分,固定长部分结构如下:

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

Next: 指向下一个MDL结构,从而构成链表,有时一个IRP会包含多个MDL

Size:MDL本身的大小,注意包含了定长部分和变长两部分的size

MdlFlags:属性标记,如所描述的物理页有没有被lock住等

Process:顾名思义,指向该包含该虚拟地址的地址空间的对应进程结构

MappedSystemVa:内核态空间中的对应地址

StartVa:用户或者内核地址空间中的虚拟地址,取决于在哪allocate的,该值是页对齐的

ByteCount:MDL所描述的虚拟地址段的大小,byte为单位

ByteOffset:起始地址的页内偏移,因为MDL所描述的地址段不一定是页对齐的

如allocate出来的虚拟地址为0xac004010,则StartVa为0xac004000,ByteOffset为0x10,MmGetMdlVirtualAddress给出StartVa + ByteOffset。

变长部分包含了物理页编号数组,可以用

PPFN_NUMBER  pfn = MmGetMdlPfnArray(mdl)

来得到,注意里面只包含了pfn,不包含页内偏移量。数组的元素个数可以由ADDRESS_AND_SIZE_TO_SPAN_PAGES得到。

jpg 改 rar 

Windows驱动中通过MDL实现用户态与核心态共享内存的更多相关文章

  1. 第二十七篇:Windows驱动中的PCI, DMA, ISR, DPC, ScatterGater, MapRegsiter, CommonBuffer, ConfigSpace

    近期有些人问我PCI设备驱动的问题, 和他们交流过后, 我建议他们先看一看<<The Windows NT Device Driver Book>>这本书, 个人感觉, 这本书 ...

  2. linux设备驱动第五篇:驱动中的并发与竟态

    综述 在上一篇介绍了linux驱动的调试方法,这一篇介绍一下在驱动编程中会遇到的并发和竟态以及如何处理并发和竞争. 首先什么是并发与竟态呢?并发(concurrency)指的是多个执行单元同时.并行被 ...

  3. Linux中的栈:用户态栈/内核栈/中断栈

    http://blog.chinaunix.net/uid-14528823-id-4136760.html Linux中有多种栈,很容易弄晕,简单说明一下: 1.用户态栈:在进程用户态地址空间底部, ...

  4. windows驱动开发详解学习笔记

    1. windows驱动分两类,NT式驱动和WDM驱动,后者支持即插即用: 2. DriverEntry是入口函数,传入参数:pDriverObject由IO管理器传入: 3. WDM驱动中,AddD ...

  5. Windows驱动派遣函数的学习

    //派遣处理例程的介绍: //IPR简介: //IRP全称(I/O Request Package),即输入输出请求包.他是windows驱动的重要概念,用户模式下所有对驱动程序的I/O请求,全部由操 ...

  6. linux 用户态和内核态以及进程上下文、中断上下文 内核空间用户空间理解

    1.特权级         Intel x86架构的cpu一共有0-4四个特权级,0级最高,3级最低,ARM架构也有不同的特权级,硬件上在执行每条指令时都会对指令所具有的特权级做相应的检查.硬件已经提 ...

  7. Linux内存管理 —— 内核态和用户态的内存分配方式

    1. 使用buddy系统管理ZONE我的这两篇文章buddy系统和slab分配器已经分析过buddy和slab的原理和源码,因此一些细节不再赘述.所有zone都是通过buddy系统管理的,buddy ...

  8. 进程:linux用户态-内核态

    用户态:Ring3运行于用户态的代码则要受到处理器的诸多检查,它们只能访问映射其地址空间的页表项中规定的在用户态下可访问页面的虚拟地址,且只能对任务状态段(TSS)中I/O许可位图(I/O Permi ...

  9. Windows系统中CreateFileMapping实现的共享内存及用法

    在32位的Windows系统中,每一个进程都有权访问他自己的4GB(232=4294967296)平面地址空间,没有段,没有选择符,没有near和far指针,没有near和far函数调用,也没有内存模 ...

随机推荐

  1. (笔记)Linux下的ioctl()函数详解

    我这里说的ioctl函数是指驱动程序里的,因为我不知道还有没有别的场合用到了它,所以就规定了我们讨论的范围.写这篇文章是因为我前一阵子被ioctl给搞混了,这几天才弄明白它,于是在这里清理一下头脑. ...

  2. 使用Photoshop实现弹簧字效果

    一.准备工作 软件环境:PhotoshopCS6 目的:使用路径实现弹簧字效果 二.实验步骤 1,新建画布 2,背景改为黑色,快捷键 Alt+Delete 3,键入文字,发现文字显示不出来,这时选择左 ...

  3. unity-------------UI的界面调节

    Rect Transform 我们都知道,Unity3D中所有的GameObject都必须要携带一个Transform组件,且该组件无法移除,那么作为UI显示的GameObject则不是携带Trans ...

  4. NHibernate 基础教程

    NHibernate之旅系列文章导航 宣传语 NHibernate.NHibernate教程.NHibernate入门.NHibernate下载.NHibernate教程中文版.NHibernate实 ...

  5. find命令/文件名后缀

    2.23/2.24/2.25 find命令 2.26 文件名后缀 find 搜索文件的命令: which   它是从环境变量中找: [root@centos_1 ~]# which ls alias ...

  6. 腾讯大渝网(esf.cq.qq.com)任意手机注册+短信验证码泄露

    地址在这里:http://esf.cq.qq.com/c=register 直接返回短信验证码, data: {data:您的手机验证码为8453,2小时内有效., auth_code:8453}ti ...

  7. 谈谈Android中的SurfaceTexture

    2015.7.2更新 由于很多人要代码,我把代码下载链接放在这里了.不过还是要说一下,surfaceTexture和OpenGL ES结合才能发挥出它最大的效果,我这种写法只是我自己的想法,还有很多种 ...

  8. 64位系统web项目导出excel问题分析及解决方法汇总

    最近在web项目中做了一个导出Excel功能.在导出的时候报错:检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件时失败. 一 ...

  9. 软件设计模式之适配器模式(JAVA)

    什么是适配器模式? 在计算机编程中,适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的.适配器能将因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存 ...

  10. SharePoint PowerShell部署开发好的WebPart到服务器上

    内容仅供参考,需结合实际需求来处理. =========SharePoint 环境下运行ps1文件,ps1内容如下======= Set-ExecutionPolicy ByPass Add-PSSn ...