zoukankan      html  css  js  c++  java
  • 驱动开发之 设备读写方式:缓冲区方式

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

    1.

    设备对象一共有三种读写方式:缓冲区方式读写(Buffered方式);直接方式读写(Direct方式);Neither方式。这三种方式的Flags分别对应DO_BUFFERED_IO,DO_DIRECT_IO,0

    在buffered方式中,I/O管理器先创建一个与用户模式数据缓冲区大小相等的系统缓冲区。而你的驱动程序将使用这个系统缓冲区工作。I/O管理器负责在系统缓冲区和用户模式缓冲区之间复制数据。 
    在direct方式中,I/O管理器锁定了包含用户模式缓冲区的物理内存页,并创建一个称为MDL(内存描述符表)的辅助数据结构来描述锁定页。因此你的驱动程序将使用MDL工作。 
    在neither方式中,I/O管理器仅简单地把用户模式的虚拟地址传递给你。而使用用户模式地址的驱动程序应十分小心。

    2.

    下面介绍缓冲区方式读写。其优点是比较简单的解决了将用户地址传入驱动的问题,缺点是需要用户模式和内核模式之间数据复制,可想而知,运行效率会受到影响。适合少量内存操作时使用的一种方法。

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

    现在以readfile为例,首先应用程序中需要提供一段缓冲区并把缓冲区大小作为参数传入,例如

    UCHAR OutputBuffer[10];
    DWORD RetLen = 0;
    readfile(hDevice,OutputBuffer,sizeof(OutputBuffer),&RetLen,NULL);

    OutputBuffer是提供的输出缓冲区,是用户模式的内存地址,操作系统将此缓冲区的数据复制到内核模式下的地址中,sizeof(OutputBuffer)是缓冲区的大小,而RetLen是真正的输出的字节数。

    那么内核模式怎么得到此内核模式地址呢?怎么得到writefile或readfile的字节数呢?答案在下面。

    此内核模式下的地址可以通过此readfile创建的IRP的AssociatedIrp.SystemBuffer得到。

    假如请求的IRP为PIRP pIrp(一般是派遣函数的参数),那么UCHAR* OutputBuffer= (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;

    而readfile请求的字节数为IO_STACK_LOCATION中的Parameters.Read.Length,writefilew为IO_STACK_LOCATION中的Parameters.Write.Length

    //得到当前堆栈
    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
    //得到readfile缓冲区大小
    ULONG cbread = stack->Parameters..Read.Length;
    //得到writefile缓冲区大小
    ULONG cbwrite = stack->Parameters.Write.Length;

    得到了内核模式下的缓冲区地址了就可以对此缓冲区操作了。比如:

    UCHAR* OutputBuffer= (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;

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

    memcpy(OutputBuffer,0xBB,cbread);

    这样用户模式下的缓冲区内得到的数据是0xBB。

    另外还要设置实际操作的字节数,pIrp->IoStatus.Information = cbread;(实际操作的字节数不一定要设置为缓冲区的大小,但也不应该大于缓冲区的大小)

    那么用户模式下readfile的RetLen被设置为cbread。

    下面是IRP_MJ_READ的派遣函数:

    NTSTATUS DispatchRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) 
    {
    	KdPrint(("Enter DispatchRead
    "));
    
    	//对一般IRP的简单操作,后面会介绍对IRP更复杂的操作
    	NTSTATUS status = STATUS_SUCCESS;
    
    	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
    	ULONG ulReadLength = stack->Parameters.Read.Length;
    	
    	// 完成IRP
    	//设置IRP完成状态
    	pIrp->IoStatus.Status = status;
    
    	//设置IRP操作了多少字节
    	pIrp->IoStatus.Information = ulReadLength;	
    
    	memset(pIrp->AssociatedIrp.SystemBuffer,0xAA,ulReadLength);
    
    	//处理IRP
    	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
    
    	KdPrint(("Leave DispatchRead
    "));
    
    	return status;
    }
  • 相关阅读:
    (15)疯狂的程序员----《绝影》
    (14)嵌入式软件开发工程师技能要求总结
    (13)碎片化阅读只会让你变得越来越愚蠢
    (12)QT中搭建opencv开发环境
    (11)git服务器的安装和配置
    (10)python学习笔记一
    (3.3)狄泰软件学院C++课程学习剖析四
    (9)Linux下gdb调试学习
    (8)Linux(客户端)和Windows(服务端)下socket通信实例
    springMVC伪静态
  • 原文地址:https://www.cnblogs.com/endenvor/p/9046545.html
Copyright © 2011-2022 走看看