驱动开发之 设备读写方式:直接方式

上一节介绍了缓冲区方式读写,这一节咱们来看看直接方式读写设备。

1.

直接方式读写设备,操作系统会将用户模式下的缓冲区锁住,然后操作系统将这段缓冲区在内核模式地址再次映射一遍。这样,用户模式的缓冲区和内核模式的缓冲区指向的是同一区域的物理内存。无论操作系统如何切换进程,内核模式地址都保持不变。

创建好设备IoCreateDevice后,需要设置DO_DIRECT_IO,  pDevObj->Flags |= DO_DIRECT_IO.

2.

这里涉及到内存描述符表(MDL)

MDL结构的声明如下:

typedef struct _MDL {

struct _MDL *Next;

CSHORT Size;

CSHORT MdlFlags;

struct _EPROCESS *Process;

PVOID MappedSystemVa;

PVOID StartVa;               //给出了用户缓冲区的虚拟地址,第一个页地址,这个地址仅在拥有数据缓冲区的用户模式进程上下文中才有效

ULONG ByteCount;       //是缓冲区的字节长度

ULONG ByteOffset;       //是缓冲区起始位置在一个页帧中的偏移值,那么缓冲区的首地址是mdl->StartVa+mdl->ByteOffset

} MDL, *PMDL;

用图表示内存描述符表(MDL)结构为:

由图可知用户模式的这段缓冲区在虚拟内存上是连续的,但在物理内存上可能是离散的。

3.下面来看一些MDL相关的函数

IoAllocateMdl 创建MDL
IoBuildPartialMdl 创建一个已存在MDL的子MDL
IoFreeMdl 销毁MDL
MmBuildMdlForNonPagedPool 修改MDL以描述内核模式中一个非分页内存区域
MmGetMdlByteCount 取缓冲区字节大小(得到mdl->ByteCount)
MmGetMdlByteOffset 取缓冲区在第一个内存页中的偏移(得到mdl->ByteOffset)
MmGetMdlVirtualAddress 取虚拟地址((PVOID)(PCHAR)(mdl->StartVa+mdl->ByteOffset))
MmGetSystemAddressForMdl 创建映射到同一内存位置的内核模式虚拟地址
MmGetSystemAddressForMdlSafe 与MmGetSystemAddressForMdl相同,但Windows 2000首选
MmInitializeMdl (再)初始化MDL以描述一个给定的虚拟缓冲区
MmPrepareMdlForReuse 再初始化MDL
MmProbeAndLockPages 地址有效性校验后锁定内存页
MmSizeOfMdl 取为描述一个给定的虚拟缓冲区的MDL所占用的内存大小
MmUnlockPages 为该MDL解锁内存页

4.下面以readfile为例介绍直接方式读取设备

用户模式调用readfile:

UCHAR OutputBuffer[10];

DWORD RetLen = 0;

readfile(hDevice,OutputBuffer,sizeof(OutputBuffer),&RetLen,NULL);

内核模式得到要读取的字节数:(与以缓冲区读写方式一样)

//得到当前堆栈

PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);

//得到readfile要读取的字节数

ULONG cbread = stack->Parameters..Read.Length;

另外,通过IRP的pIrp->MdlAddress得到MDL数据结构,这个结构描述了被锁定的缓冲区的内存。

下面是一个IRP_MJ_READ的派遣函数,仅供参考。

NTSTATUS DispathRead(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp)
{
	KdPrint(("Enter DispathRead\n"));

	PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
	NTSTATUS status = STATUS_SUCCESS;

 	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);

 	ULONG ulReadLength = stack->Parameters.Read.Length;//得到读取的长度
	KdPrint(("ulReadLength:%d\n",ulReadLength));

	ULONG mdl_length = MmGetMdlByteCount(pIrp->MdlAddress);       //mdl虚拟内存的长度
	PVOID mdl_address = MmGetMdlVirtualAddress(pIrp->MdlAddress); //虚拟内存的起始地址
	ULONG mdl_offset = MmGetMdlByteOffset(pIrp->MdlAddress);      //虚拟内存首地址在第一页的偏移量

	KdPrint(("mdl_address:0X%08X\n",mdl_address));
	KdPrint(("mdl_length:%d\n",mdl_length));
	KdPrint(("mdl_offset:%d\n",mdl_offset));

	if (mdl_length!=ulReadLength)
	{
		//MDL的长度应该和读长度相等,否则该操作应该设为不成功
		pIrp->IoStatus.Information = 0;
		status = STATUS_UNSUCCESSFUL;
	}else
	{
		//用MmGetSystemAddressForMdlSafe得到MDL在内核模式下的映射,被映射到内核模式下的内存地址,必定在0X80000000-0XFFFFFFFF之间
		PVOID kernel_address = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
		KdPrint(("kernel_address:0X%08X\n",kernel_address));
		memset(kernel_address,0XAA,ulReadLength);        //对内核模式下的内存地址进行操作
		pIrp->IoStatus.Information = ulReadLength;	//设置实际操作字节数
	}

	pIrp->IoStatus.Status = status;

	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
	KdPrint(("Leave DispatchRead\n"));

	return status;
}

驱动开发之 设备读写方式:缓冲区方式 请参考:http://blog.csdn.net/liyun123gx/article/details/38042125

NEITHER方式参考:http://blog.csdn.net/liyun123gx/article/details/38046865

时间: 2024-12-24 03:45:55

驱动开发之 设备读写方式:直接方式的相关文章

驱动开发之 设备读写方式:缓冲区方式

1. 设备对象一共有三种读写方式:缓冲区方式读写(Buffered方式):直接方式读写(Direct方式):Neither方式.这三种方式的Flags分别对应DO_BUFFERED_IO,DO_DIRECT_IO,0 在buffered方式中,I/O管理器先创建一个与用户模式数据缓冲区大小相等的系统缓冲区.而你的驱动程序将使用这个系统缓冲区工作.I/O管理器负责在系统缓冲区和用户模式缓冲区之间复制数据. 在direct方式中,I/O管理器锁定了包含用户模式缓冲区的物理内存页,并创建一个称为MDL

Linux 设备驱动开发 —— platform设备驱动应用实例解析

前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 -- platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platform模型驱动编程,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳. 在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中.那么我们编写platform模型驱动时,需要完成两个工作: a -- 实现platform驱动 架构就

Linux设备驱动开发 - 平台设备驱动

Linux2.6的内核中引入了一种新的设备驱动模型-平台(platform)设备驱动,平台设备驱动分为平台设备(platform_device)和平台驱动(platform_driver),平台设备的引入使得Linux设备驱动更加便于移植. 一.平台设备平台设备结构体: 1 struct platform_device { 2 const char * name; /* 设备名 */ 3 int id; 4 struct device dev; /* 设备结构体 */ 5 u32 num_res

驱动开发--【字符设备、块设备简介】【sky原创】

驱动开发   字符设备,块设备,网络设备 字符设备 以字节流的方式访问, 不能随机访问 有例外,显卡.EEPROM可以随机访问 EEPROM可以擦写1亿次,是一种字符设备,可以随机访问 读写是直接访问硬件的 flash 擦写次数有限,一百万次,容易有坏块 块设备 能随机访问 以”块“为单位进行访问 块大小一般为512字节 块的大小由硬件决定 是内核进行数据传输的基本单位 硬盘结构: 格式化分区是以柱面为单位的,即硬盘的柱面 如果有10个盘面,就有十个柱面 对于嵌入式设备 如果是flash的话,结

Andriod驱动开发与系统移植概述读后感

本章通过介绍android系统架构->介绍android系统移植的主要工作->查看linux内核版本->linux内核版本号的定义规则->如何学习linux驱动开发->linux设备驱动    给linux驱动举例的方式来对于Andriod系统移植与驱动开发的工作做出一个基本的概述. 首先,android移植可以分为:应用移植和系统移植,应用移植是将程序移植到某一个特定硬件平台上:系统移植是指让android是指让android操作系统支持硬件平台上运行.那么对于嵌入式系统来

Windows驱动开发-设备读写方式

设备读写方式共三种: 方式 Flag 特点 缓冲区方式读写 DO_BUFFERED_IO I/O管理器先创建一个与用户模式数据缓冲区大小相等的系统缓冲区.而你的驱动程序将使用这个系统缓冲区工作.I/O管理器负责在系统缓冲区和用户模式缓冲区之间复制数据. 直接方式读写 DO_DIRECT_IO I/O管理器锁定了包含用户模式缓冲区的物理内存页,并创建一个称为MDL(内存描述符表)的辅助数据结构来描述锁定页.因此你的驱动程序将使用MDL工作. Neither方式 0 I/O管理器仅简单地把用户模式的

NX二次开发-基于NX开发向导模板的NX对Excel读写操作(OLE方式(COM组件))

在看这个博客前,请读者先去完整看完:NX二次开发-基于MFC界面的NX对Excel读写操作(OLE方式(COM组件))https://ufun-nxopen.blog.csdn.net/article/details/88922030 这篇博客,要不然你听不懂我下面在说什么. 版本NX11+VS2013+office2016 首先我们通过NX开发向导创建了一个模板. 先把项目属性改成多字节.下面我们把前面做的MFC项目里的几个EXCAL头文件和stdafx一块拷过来,加到NX的项目里. 在NX的

NX二次开发-基于MFC界面的NX对Excel读写操作(OLE方式(COM组件))

NX二次开发API里没有对EXCAL读写操作的相关函数,市面上有很多种方法去实现,比如UFUN调KF,ODBC,OLE(COM组件)等等.这里我是用的OLE(COM组件)方式去做的,这种在VC上创建的方法,无论C++还是C#还是VB方式思路都是一样的.先介绍用MFC去做,然后在写一篇博客介绍怎么在NX的二次开发的向导模板里去做.NX二次开发-基于NX开发向导模板的NX对Excel读写操作(OLE方式(COM组件))https://ufun-nxopen.blog.csdn.net/article

Linux设备驱动开发基础

1.驱动概述和开发环境搭建 1.1驱动设备的作用 对设备驱动最通俗的解释就是"驱动硬件设备行动".驱动与底层硬件直接打交道,按照硬件设备的具体工作方式,读写设备的寄存器,完成设备的轮训.中断处理.DMA通信,进行物理内存向虚拟内存的映射等,最终让通信设备能收发数据,让显示设备能显示文字和画面,让存储设备能记录文件和数据. 由此可见,设备驱动充当了硬件和应用软件之间的纽带,他使得应用软件只需要调用系统软件的应用编程接口(API)就可让硬件去完成要求的工作.在系统中没有操作系统的情况下,工