当前位置: 首页 > article >正文

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

        我们已经明白了缓冲区方式的读写操作,下面根据这部分知识,来编写一个虚拟设备。这个设备来模拟一个文件,可以将这个设备想象成一个普通文件,可以进行读写作。另外,每次写这个文件,文件的长度会增加,可以利用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;
}


 


http://www.kler.cn/a/311579.html

相关文章:

  • 使用Matlab神经网络工具箱
  • LLMs:MindFormers的简介、安装和使用方法、案例应用
  • uniapp的基本使用(easycom规范和条件编译)和uview组件的安装和使用
  • Vue 中的定时刷新与自动更新实现
  • MYSQL隔离性原理——MVCC
  • 11个简单易用的电商购物车设计案例
  • 服务器数据恢复—raid5阵列热备盘上线失败导致阵列崩溃的数据恢复案例
  • redis为什么不使用一致性hash
  • 向日葵好用吗?4款稳定的远程控制软件推荐。
  • 关于交叉编译移植到Debian开发板的一些随笔
  • gbase8s存储过程一些隐藏的错误写法
  • docker镜像源
  • info 命令:查看命令手册
  • 案例分析-Stream List 中取出值最大的前 5 个和最小的 5 个值
  • 动态内存
  • 7.Java高级编程 多线程
  • flutter Dio发送post请求
  • Linux: debug:内核log有乱码^@^@
  • Redis——分布式锁
  • JVM 虚拟机的编译器、类加载过程、类加载器有哪些?
  • Paragon NTFS for Mac和Tuxera NTFS for Mac,那么两种工具有什么区别呢?
  • python中的排序函数sorted
  • 波分技术基础 -- MS-OTN介绍
  • AIGC论文查重是什么?
  • 【Verilog学习日常】—牛客网刷题—Verilog快速入门—VL23
  • C++笔记21•C++11的新特性•