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

驱动和应用层的三种通信方式

驱动程序和客户 应用程序经常需要进行数据交换,但我们知道 驱动程序和客户 应用程序可能不在同一个地址空间,因此操作系统必须解决两者之间的数据交换。
驱动和应 用层 通信,主要是靠DeviceIoControl函数,下面是该函数的原型:
BOOL DeviceIoControl ( 
HANDLE hDevice, // 设备句柄 
DWORD dwIoControlCode, // IOCTL请求操作代码 
LPVOID lpInBuffer, // 输入缓冲区地址 
DWORD nInBufferSize, // 输入缓冲区大小 
LPVOID lpOutBuffer, // 输出缓冲区地址 
DWORD nOutBufferSize, // 输出缓冲区大小 
LPDWORD lpBytesReturned, // 存放返回字节数的指针 
LPOVERLAPPED lpOverlapped // 用于同步操作的Overlapped结构体指针 
);

dwIoControlCode
要进行操作的控制码。 驱动程序可以通过CTL_CODE宏来组合定义一个控制码,并在IRP_MJ_DEVICE_CONTROL的实现中进行控制码的操作。在 驱动层,irpStack->Parameters.DeviceIoControl.IoControlCode表示了这个控制码。

IOCTL请求有四种缓冲策略,下面一一介绍。 
1、 输入输出缓冲I/O(METHOD_BUFFERED)
2、 直接输入缓冲输出I/O(METHOD_IN_DIRECT)
3、 缓冲输入直接输出I/O(METHOD_OUT_DIRECT)
4、 上面 三种方法都不是(METHOD_NEITHER)

为了对这些类型更详细的描述,请看msdn上的解释,我抄录如下:

"缓冲"方法(METHOD_BUFFERED)
备注:在下面的讨论中,"输入"表示数据从用户模 应用程序到 驱动程序,"输出"表示数据从 驱动程序到 应用程序。

对于读取请求,I/O 管理器分配一个与用户模 的缓冲区大小相同的系统缓冲区。IRP 中的 SystemBuffer 字段包含系统地址。UserBuffer 字段包含初始的用户缓冲区地址。当完成请求时,I/O 管理器将 驱动程序已经提供的数据从系统缓冲区复制到用户缓冲区。对于写入请求,会分配一个系统缓冲区并将 SystemBuffer 设置为地址。用户缓冲区的内容会被复制到系统缓冲区,但是不设置 UserBuffer。对于 IOCTL 请求,会分配一个容量大小足以包含输入缓冲区或输出缓冲区的系统缓冲区,并将 SystemBuffer 设置为分配的缓冲区地址。输入缓冲区中的数据复制到系统缓冲区。UserBuffer 字段设置为用户模 输出缓冲区地址。内核模 驱动程序应当只使用系统缓冲区,且不应使用 UserBuffer 中存储的地址。

对于 IOCTL, 驱动程序应当从系统缓冲区获取输入并将输出写入到系统缓冲区。当完成请求时,I/O 系统将输出数据从系统缓冲区复制到用户缓冲区。

"直接"方法(METHOD_IN/OUT_DIRECT)
对于读取和写入请求,用户模 缓冲区会被锁定,并且会创建一个内存描述符列表 (MDL)。MDL 地址会存储在 IRP 的 MdlAddress 字段中。SystemBuffer 和 UserBuffer 均没有任何含义。但是, 驱动程序不应当更改这些字段的值。

对于 IOCTL 请求,如果在 METHOD_IN_DIRECT 和 METHOD_OUT_DIRECT 中同时有一个输出缓冲区,则分配一个系统缓冲区(SystemBuffer 又有了地址)并将输入数据复制到其中。如果有一个输出缓冲区,且它被锁定,则会创建 MDL 并设置 MdlAddress。UserBuffer 字段没有任何含义。

"两者都不"方法(METHOD_NEITHER)
对于读取和写入请求,UserBuffer 字段被设置为指向初始的用户缓冲区。不执行任何其他操作。SystemAddress 和 MdlAddress 没有任何含义。对于 IOCTL 请求,I/O 管理器将 UserBuffer 设置为初始的用户输出缓冲区,而且,它将当前 I/O 栈位置的 Parameters.DeviceIoControl.Type3InputBuffer 设置为用户输入缓冲区。利用该 I/O 方法,由 驱动程序来确定如何处理缓冲区:分配系统缓冲区或创建 MDL。

通常, 驱动程序在访问用户数据时不应当将 UserBuffer 字段用作地址,即使当用户缓冲区被锁定时也是如此。这是由于在调用 驱动程序时,在系统中可能看不到调用用户的地址空间。(对于该规则的一个例外是,在最高层 驱动程序将 IRP 向下传递到较低 层的 驱动程序之前,它可能需要使用 UserBuffer 来复制数据。)如果使用"直接"或"两者都不"方法,在创建 MDL 之后, 驱动程序可以使用 MmGetSystemAddressForMdl 函数来获取有效的系统地址以访问用户缓冲区。


驱动层,依传输类型的不同,输入缓冲区的位置亦不同,见下表。
传输类型                               位置
METHOD_IN_DIRECT                irp->AssociatedIrp.SystemBuffer
METHOD_OUT_DIRECT             irp->AssociatedIrp.SystemBuffer
METHOD_BUFFERED                 irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER                   irpStack->Parameters.DeviceIoControl.Type3InputBuffer

驱动层,依传输类型的不同,输出缓冲区的位置亦不同,见下表。
传输类型                              位置
METHOD_IN_DIRECT                irp->MdlAddress
METHOD_OUT_DIRECT             irp->MdlAddress
METHOD_BUFFERED                 irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER                    irp->UserBuffer

所以只要确定了传输 方式后,就可以根据各自的位置来读取和写入数据,从而实现 应用层和 驱动通信
下面看 驱动层对ioctl控制码的处理代码:

代码:
//METHOD_OUT_DIREC方式
NTSTATUS COMM_DirectOutIo(PIRP Irp, PIO_STACK_LOCATION pIoStackIrp, UINT *sizeofWrite)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    PVOID pInputBuffer, pOutputBuffer;
  ULONG  outputLength, inputLength;

    DbgPrint("COMM_DirectOutIo\r\n");

    outputLength = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;
    inputLength  = pIoStackIrp->Parameters.DeviceIoControl.InputBufferLength;
    pInputBuffer = Irp->AssociatedIrp.SystemBuffer;
    pOutputBuffer = NULL;

    if(Irp->MdlAddress)
        pOutputBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);

    if(pInputBuffer && pOutputBuffer)
    {                                                          
        DbgPrint("COMM_DirectOutIo UserModeMessage = '%s'", pInputBuffer);
        RtlCopyMemory(pOutputBuffer, pInputBuffer, outputLength);
    *sizeofWrite = outputLength;
        status = STATUS_SUCCESS;
    }
    return status;
}

// METHOD_IN_DIRECT
NTSTATUS COMM_DirectInIo(PIRP Irp, PIO_STACK_LOCATION pIoStackIrp, UINT *sizeofWrite)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    PVOID pInputBuffer, pOutputBuffer;
  ULONG  outputLength, inputLength;

    DbgPrint("COMM_DirectInIo\r\n");

    outputLength = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;
    inputLength  = pIoStackIrp->Parameters.DeviceIoControl.InputBufferLength;
    pInputBuffer = Irp->AssociatedIrp.SystemBuffer;
    pOutputBuffer = NULL;

    if(Irp->MdlAddress)
        pOutputBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);

    if(pInputBuffer && pOutputBuffer)
    {                                                          
        DbgPrint("COMM_DirectInIo UserModeMessage = '%s'", pInputBuffer);
        RtlCopyMemory(pOutputBuffer, pInputBuffer, outputLength);
        *sizeofWrite = outputLength;
        status = STATUS_SUCCESS;
    }
    return status;
}

// METHOD_BUFFERED
NTSTATUS COMM_BufferedIo(PIRP Irp, PIO_STACK_LOCATION pIoStackIrp, UINT *sizeofWrite)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    PVOID pInputBuffer, pOutputBuffer;
  ULONG  outputLength, inputLength;

    DbgPrint("COMM_BufferedIo\r\n");

  outputLength = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;
    inputLength  = pIoStackIrp->Parameters.DeviceIoControl.InputBufferLength;
    pInputBuffer = Irp->AssociatedIrp.SystemBuffer;
    pOutputBuffer = Irp->AssociatedIrp.SystemBuffer;

    if(pInputBuffer && pOutputBuffer)
    {              
    DbgPrint("COMM_BufferedIo UserModeMessage = '%s'", pInputBuffer);
        RtlCopyMemory(pOutputBuffer, pInputBuffer, outputLength);
    *sizeofWrite = outputLength;
        status = STATUS_SUCCESS;
    }
    return status;
}

// METHOD_NEITHER
NTSTATUS COMM_NeitherIo(PIRP Irp, PIO_STACK_LOCATION pIoStackIrp, UINT *sizeofWrite)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    PVOID pInputBuffer, pOutputBuffer;
  ULONG  outputLength, inputLength;

    DbgPrint("COMM_NeitherIo\r\n");

  outputLength  = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;
    inputLength   = pIoStackIrp->Parameters.DeviceIoControl.InputBufferLength;
    pInputBuffer  = pIoStackIrp->Parameters.DeviceIoControl.Type3InputBuffer;
    pOutputBuffer = Irp->UserBuffer;

    if(pInputBuffer && pOutputBuffer)
    {              
    DbgPrint("COMM_NeitherIo UserModeMessage = '%s'", pInputBuffer);
        RtlCopyMemory(pOutputBuffer, pInputBuffer, outputLength);
    *sizeofWrite = outputLength;
        status = STATUS_SUCCESS;
    }
    return status;
}

代码比较简单,都是取得输入的数据,然后把数据直接拷贝到输出,传输给应用层。

应用层的代码:
procedure TfrmMain.Send_Recv_Data(AInData: String; var AOutData:String;
  IoctlCode: DWORD);
var
  dwReturn: DWORD;
  inData:array[0..1023] of char;
  outData:array[0..1023] of char;
begin
  StrPCopy(inData, AInData);
  if m_hCommDevice <> 0 then
  begin
    DeviceIoControl(m_hCommDevice, IoctlCode, @inData,  Length(inData), @outData, Length(outData), dwReturn, nil);
    AOutData := StrPas(@outData);
  end;
end;
上面是进行发送和接受的过程。
需要 通信,只要如下做:
代码:
procedure TfrmMain. btnDirect_IN_IOClick (Sender: TObject);
var
  outData:String;
begin
  Send_Recv_Data(Trim(edtDirect_in_in.Text), outData, IOCTL_COMM_DIRECT_IN_IO);
  edtDirect_in_out.Text := outData;
end;
这是 direct_in 方式 通信,其他 通信 方式类似,大家可以参考代码了,这里就不列举了,由于代码比较简单,我就不多说了,大家还是看代码吧,很好明白。最后,给个测试图:

应用层:
名称:  1.jpg查看次数: 3798文件大小:  27.9 KB
驱动层:
点击图片以查看大图图片名称: 2.jpg查看次数: 3969文件大小: 34.9 KB文件 ID : 10930

代码:
驱动和应用层通信.rar. 

转载于:https://www.cnblogs.com/vcerror/p/4289077.html

相关文章:

  • 《Java编程思想》笔记03------访问权限控制
  • 记笔记与博客
  • 树的遍历
  • 菜鸟配置SAMBA服务之1
  • Qt动态设置布局中的控件
  • CISCO无线AP胖瘦升级
  • dotcms总结
  • 设计模式的原则
  • electron
  • 基于BIND实现DNS的解析、主从、子域、请求转发、访问控制
  • Mysql初始化root密码和允许远程访问
  • 软件项目隐形成本
  • MyBatis返回类型resultType和resultMap
  • 一个简单的git应用教程
  • CSS3 动画及过渡详解
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • angular学习第一篇-----环境搭建
  • egg(89)--egg之redis的发布和订阅
  • IP路由与转发
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • LeetCode18.四数之和 JavaScript
  • markdown编辑器简评
  • MySQL QA
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • Python进阶细节
  • React-生命周期杂记
  • 彻底搞懂浏览器Event-loop
  • 读懂package.json -- 依赖管理
  • 世界编程语言排行榜2008年06月(ActionScript 挺进20强)
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • FaaS 的简单实践
  • 交换综合实验一
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • (1)安装hadoop之虚拟机准备(配置IP与主机名)
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第5节(封闭类和Final方法)
  • (九)One-Wire总线-DS18B20
  • (九十四)函数和二维数组
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (三)elasticsearch 源码之启动流程分析
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (推荐)叮当——中文语音对话机器人
  • (转)linux 命令大全
  • (转)Linux下编译安装log4cxx
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .NET CORE 第一节 创建基本的 asp.net core
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)
  • .NetCore实践篇:分布式监控Zipkin持久化之殇
  • .NET委托:一个关于C#的睡前故事
  • @requestBody写与不写的情况
  • [04] Android逐帧动画(一)
  • [C#] 如何调用Python脚本程序
  • [docker] Docker的数据卷、数据卷容器,容器互联
  • [English]英语积累本