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. elasticsearch系列五:搜索详解(查询建议介绍、Suggester 介绍)

    一.查询建议介绍 1. 查询建议是什么? 查询建议,为用户提供良好的使用体验.主要包括: 拼写检查: 自动建议查询词(自动补全) 拼写检查如图: 自动建议查询词(自动补全): 2. ES中查询建议的A ...

  2. Java如何查看线程的优先级?

    Java编程中,如何查看线程的优先级? 以下示例演示如何使用Thread类的getPriority()方法检查线程的优先级. package com.yiibai; public class Thre ...

  3. e826. 获得和设置JSplitPane分开的位置

    The location of a divider is measured in pixels from either the left edge (in the case of a horizont ...

  4. e856. 列出一个组件的所有事件

    This example demonstrates how to list all the actions in a component. ActionMap map = component.getA ...

  5. linq join 左连接 leftjoin 多个on条件 where 条件

    var haveChange = from newScore in newScoreList join oldScore in oldScoreList on new{newScore.ExamId, ...

  6. Tomcat5内存简单优化

    tomcat版本:apache-tomcat-5.5.28 解压版tomcat 修改catalina.bat文件,在开头添加如下内容: JAVA_OPTS='-Xms1024m -Xmx2048m - ...

  7. java.lang.IllegalArgumentException: Request header is too large 解决方案

    错误描述: java.lang.IllegalArgumentException: Request header is too large 问题分析: 请求头超过了tomcat的限值.本来post请求 ...

  8. spring + springMVC + spring Data + jpa + maven 项目框架搭建

    首先看一下项目结构: 所用到的jar(pom.xml): <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:x ...

  9. scala分析数据作图

    参考网址:https://stackoverflow.com/questions/36984780/spark-shell-how-to-use-breeze-viz 刚开始按照网上的教程只导入了 两 ...

  10. Android Material Design控件学习(三)——使用TextInputLayout实现酷市场登录效果

    前言 前两次,我们学习了 Android Material Design控件学习(一)--TabLayout的用法 Android Material Design控件学习(二)--Navigation ...