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

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

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

    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
    "));
    
    	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
    ",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
    ",mdl_address));
    	KdPrint(("mdl_length:%d
    ",mdl_length));
    	KdPrint(("mdl_offset:%d
    ",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
    ",kernel_address));
    		memset(kernel_address,0XAA,ulReadLength);        //对内核模式下的内存地址进行操作
    		pIrp->IoStatus.Information = ulReadLength;	//设置实际操作字节数
    	}
    	
    	pIrp->IoStatus.Status = status;
    	
    	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
    	KdPrint(("Leave DispatchRead
    "));
    
    	return status;
    }
  • 相关阅读:
    6、CC2541修改按键调节广播发送功率例程为持续发送4DB的蓝牙基站
    [nRF51822] 16、nRF51822的随机数生成器,及随机数生成器的一些知识(可以帮您补补随机数发生器的知识)
    [PCB设计] 4、BAT脚本处理AD生成的GERBER文件为生产文件
    [异常解决] 奇巧淫技——VirtualBox中的linux无显示启动,并在win7上远程控制
    [PCB设计] 3、用CAM350修改GERBER文件(删除某些部分)
    [异常解决] Make nRF51 DFU Project Appear "fatal error: uECC.h: No such file or directory"
    [异常解决] How to build a gcc toolchain for nRF51 on linux (very detailed!!!)
    [异常解决] windows用SSH和linux同步文件&linux开启SSH&ssh client 报 algorithm negotiation failed的解决方法之一
    [模拟电路] 2、Passive Band Pass Filter
    Docker常用命令
  • 原文地址:https://www.cnblogs.com/endenvor/p/9046553.html
Copyright © 2011-2022 走看看