首页 > 其他分享 >派遣函数 - 缓冲区设备模拟文件读写

派遣函数 - 缓冲区设备模拟文件读写

时间:2024-09-19 14:50:26浏览次数:5  
标签:status INFORMATION IRP 读写 缓冲区 派遣 设备 pIrp

        我们已经明白了缓冲区方式的读写操作,下面根据这部分知识,来编写一个虚拟设备。这个设备来模拟一个文件,可以将这个设备想象成一个普通文件,可以进行读写作。另外,每次写这个文件,文件的长度会增加,可以利用GetFileSize函数(API函数)得到该文件的长度。

        为了实现这个目的,需要编写三个IRP的派遣函数。它们分别对应着写操作、读操作获取文件长度操作。下面分别进行介绍。

(1)写操作。在应用程序中,通过 WriteFile 函数对设备进行写操作。下面程序片级是应用程序分配10字节的缓冲区,然后将缓冲区填写0XBB,然后将这10个字节写入设备。

    UCHAR buffer[10];
    memset(buffer, 0xBB, 10);
    ULONG ulRead;
    ULONG ulWrite;
    BOOL bRet = WriteFile(hDevice,buffer,10,&ulWrite,NULL);
    if (bRet)
    {
        printf("Write %d bytes\n",ulWrite);
    }


        WriteFile 内部会创建产生 IRP_MJ_WRITE 类型的 IRP,操作系统会将这个 IRP 传递给驱动程序。IRP_MJ_WRITE的 派遣函数需要将传送进来的数据保存起来,以便读取该设备的时候读取。在本例中,这个数据存储在一个缓冲区中,缓冲区的地址记录在设备扩展中,在设备启动的时候,驱动程序负责分配这个缓冲区,在设备被卸的时候,驱动程序回收该缓冲区。

        对于IRP_MJ_WRITE 的派遣函数,主要任务是将写入的数据存储在这段缓冲区中。如果写入的字节数过大,超过缓冲区的大小,派遣函数将 IRP 的状态设置成错误状态。分外,在设备扩展中有一个变量记录着这个虚拟文件设备的文件长度。对设备的写操作会更改这个变量。

NTSTATUS HelloDDKWrite(IN PDEVICE_OBJECT pDevObj,
    IN PIRP pIrp)
{
    KdPrint(("Enter HelloDDKWrite\n"));
    NTSTATUS status = STATUS_SUCCESS;
    
    PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
    
    // 获取存储的长度
    ULONG ulWriteLength = stack->Parameters.Write.Length;

    // 获取存储的偏移量
    ULONG  ulWriteOffset = (ULONG)stack->Parameters.Write.ByteOffset.QuadPart;

    if (ulWriteOffset + ulWriteLength > MAX_FILE_LENGTH)
    {
        // 如果存储长度 + 偏移量大于缓冲区长度,则返回无效
        status = STATUS_FILE_INVALID;
        ulWriteLength = 0;
    }
    else
    {
        // 将写入的数据,存储在缓冲区内
        memcpy(pDevExt->buffer + ulWriteOffset, pIrp->AssociatedIrp.SystemBuffer, ulWriteLength);

        status = STATUS_SUCCESS;

        // 设置信的文件长度
        if (ulWriteLength + ulWriteOffset > pDevExt->file_length)
        {
            pDevExt->file_length = ulWriteLength + ulWriteOffset;
        }
    }

    //设置IRP完成状态
    pIrp->IoStatus.Status = status;

    //设置IRP操作了多少字节
    pIrp->IoStatus.Information = ulWriteLength;    // bytes xfered

    //处理IRP
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);

    KdPrint(("Leave HelloDDKRead\n"));

    return status;
}


(2) 读操作。在应用程序中,通过 ReadFile 从设备读取数据,代码如下。

    BOOL bRet = ReadFile(hDevice,buffer,10,&ulRead,NULL);
    if (bRet)
    {
        printf("Read %d bytes:",ulRead);
        // 显示读取的数据
        for (int i=0;i<(int)ulRead;i++)
        {
            printf("%02X ",buffer[i]);
        }

        printf("\n");
    }


        ReadFile 内部会创建 IRP_MJ_READ 类型的 IRP,操作系统会将这个IRP传递给驱动程序中IRP_MJ_READ的派遣函数。IRP_MJ_READ 的派道函数的主要任务是把记录的数据复制到 AssociatedIrp.SystemBuffer 中。

NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
    IN PIRP pIrp)
{
    KdPrint(("Enter HelloDDKRead\n"));
    PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

    //对一般IRP的简单操作,后面会介绍对IRP更复杂的操作
    NTSTATUS status = STATUS_SUCCESS;

    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
    ULONG ulReadLength = stack->Parameters.Read.Length;
    ULONG ulReadOffset = (ULONG)stack->Parameters.Read.ByteOffset.QuadPart;


    if (ulReadOffset + ulReadLength > MAX_FILE_LENGTH)
    {
        // 如果存储长度 + 偏移量大于缓冲区长度,则返回无效
        status = STATUS_FILE_INVALID;
        ulReadLength = 0;
    }
    else
    {
        // 将的数据存储在AssociatedIrp.SystemBuffer,以便应用程序使用
        memcpy(pIrp->AssociatedIrp.SystemBuffer, pDevExt->buffer + ulReadLength, ulReadLength);

        status = STATUS_SUCCESS;
    }

    //设置IRP完成状态
    pIrp->IoStatus.Status = status;

    //设置IRP操作了多少字节
    pIrp->IoStatus.Information = ulReadLength;    // bytes xfered


    //结束IRP
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);

    KdPrint(("Leave HelloDDKRead\n"));

    return status;
}


(3) 读取文件长度,读取文件长度依靠 GetFileSize Win32API获得。GetFileSize内容会创建IRP_MJ_QUERY_INFORMNTON类型的IRP。这个IRP请求的作用是向设备查询一些信息,这包括育询文件长度、设备创建的时间、设备属性等。在本例中IRP_MJ_QUERY_INFORMATON 派遭函数的主要任务是告诉应用程序这个设备的长度。

        IRP_MJ_QUERY_INFORMATION 可以获得不同的信息,每种信息对应一种类多类别号,该类别号是一个 PILE_INFORMATION_CLASS 类型的枚举变量,可以读取I/O堆栈的
Parameters.QueryFile.FileInformationClass 子域。查询设备长度时,也就是调用 Win32 API GetFileSize 时, Parameters.QueryFile.FileInformationClass 应该为 FileStandardInformation。

        这时,IRP的缓冲区数据为 FILE_STANDARD_INFORMATION 数据结构的数据。
    

typedef struct _FILE_STANDARD_INFORMATION {
    LARGE_INTEGER AllocationSize;
    LARGE_INTEGER EndOfFile;
    ULONG NumberOfLinks;
    BOOLEAN DeletePending;
    BOOLEAN Directory;
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;

        其中,EndOfFile子域指明设备长度,修改这个子域会在 GetFileSize的返回值中得到体现,
IRP_MJ_QUERY_INFORMATION 派遣函数如下。

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

    // 获得IO堆栈
    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
    
    // 获得设备扩展
    PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

    // 得到文件信息
    FILE_INFORMATION_CLASS info = stack->Parameters.QueryFile.FileInformationClass;

    // 判断是否标准文件信息
    if (info == FileStandardInformation)
    {
        KdPrint(("FileStandardInformation\n"));

        PFILE_STANDARD_INFORMATION file_info =
            (PFILE_STANDARD_INFORMATION)pIrp->AssociatedIrp.SystemBuffer;
        file_info->EndOfFile = RtlConvertUlongToLargeInteger(pDevExt->file_length);
    }

    NTSTATUS status = STATUS_SUCCESS;

    //设置IRP完成状态
    pIrp->IoStatus.Status = status;

    //设置IRP操作字节数
    pIrp->IoStatus.Information = stack->Parameters.QueryFile.Length;    // bytes xfered
    
    //结束IRP
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);

    KdPrint(("Leave HelloDDKQueryInformation\n"));

    return status;
}


 

标签:status,INFORMATION,IRP,读写,缓冲区,派遣,设备,pIrp
From: https://blog.csdn.net/wendyWJGU/article/details/142360371

相关文章

  • stm32 Unix时间戳&BKP备份寄存器&RTC实时时钟(读写备份寄存器&实时时钟)
    理论1.Unix时间戳(1)Unix时间戳Unix时间戳(UnixTimestamp)定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数,不考虑闰秒时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量世界上所有时区的秒计数器相同,不同时区通过添加偏移来得到当地时间 (2)UTC/GMTGM......
  • Pandas的读写数据
    目录读写文件的类型Excel写API准备数据1.直接写入(默认有索引和标题)2.写入(去掉索引)3.写入(去掉索引和标题)4.写入(去掉索引和标题,指定表单信息) Excel读API1.读(默认带有索引和标题)2.读(指定索引项)3.读(碰到无标题列和无索引列,指定索引列,标题列)4.读(只......
  • Hadoop(十一)HDFS 读写数据流程
    HDFS读写数据流程一、写数据流程1、客户端通过DistributedFileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在2、NameNode返回是否可以上传3、客户端请求第一个Block上传到哪几个DataNode服务器上4、NameNode返回3个DataNode节点,分别为dn1......
  • 使用CUBE_MX使用I2C通信,实现对EEPROM的读写
    一、使用CUBE_MX配置1.配置I2C2.配置USART13.重中之重(在KEIL5打开串口使用的库)二、KEIL5配置#include"main.h"#include"i2c.h"#include"gpio.h"#include"usart.h"#include<stdio.h>voidSystemClock_Config(void);voidI2C_EE_Buf......
  • Java多种方式实现 有界缓冲区下的多个生产者、消费者模型 (Semaphore、while+wait+noti
    /**@Author:SongyangJi@ProjectName:[email protected]@Description:*/classProducerThreadextendsThread{intrate;MultiProducerConsumermultiProducerConsumer;publicProducerThread(intrate,MultiProducerConsumermultiProducer......
  • 一款EF Core下高性能、轻量级针对分表分库读写分离的解决方案
    前言今天大姚给大家分享一款EFCore下高性能、轻量级针对分表分库读写分离的解决方案,开源(ApacheLicense)的EFCore拓展程序包:ShardingCore。ShardingCore项目介绍ShardingCore是一款开源、简单易用、高性能、普适性,针对EFCore生态下的分表分库的扩展解决方案,支持EFCore2+的所有版......
  • JAVA 多线程基础:JAVA中double 和 long非原子读写问题
    在解释这个问题之前,我们先来回顾下Java中基础数据类型所占的位数。类型长度(位)字节byte41boolean41int324short162long648char162float324double648可以看到对于double以及long两种基本数据类型,所占位数为64位。而JVM却有32bit与64bit两种,也就是说在32bitJVM中不能将doub......
  • 内存对齐和缓冲区溢出攻击
    一、问候语欢迎你来到我的博客!二、什么是内存对齐  计算机中内存空间都是按照字节(byte)进行划分的,所以从理论上讲对于任何类型的变量访问都可以从任意地址开始,但是在实际情况中,在访问特定类型变量的时候经常在特定的内存地址访问,所以这就需要把各种类型数据按照一定的规则......
  • 这才是我想要的PCIe 5.0 SSD!慧荣SM2508主控首测:读写满血 还不烫手
    市面上现有的PCIe5.0SSD几乎都采用了群联E26主控,不但读写速度达不到满血标准,最高也就12GB/s,功耗和发热还特别高,经常需要主动风扇散热。英韧IG5666性能好了不少,基本可以跑满,但是发热仍然太高,因为它俩都是台积电12nm。慧荣已经多次展示过他们的方案SM2580,一方面性能满血,一方面发......
  • Git缓冲区理解:`index`,`add`和`reset`,`staged`和`unstaged`
    在git里面,有一个叫index的区域,你把东西加到那里叫add,把东西再从哪里撤回来叫reset;已经在里面的我们形容它是staged,还没有加进去的我们形容它是unstaged。其实index区就是一个纯粹的缓冲区,也叫stagingarea,是正式提交之前给我们的一个缓冲,还有犹豫的余地。因为一旦正式commit提交......