设备读写方式:直接方式

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

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; }

 

其它方式:

设备读写方式:缓冲区方式

设备读写方式:Neither方式

 

原文链接: http://blog.csdn.net/liyun123gx/article/details/38043849

转载于:https://www.cnblogs.com/DreamOfGalaxy/articles/4242166.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值