`
huozheleisi
  • 浏览: 1235597 次
文章分类
社区版块
存档分类
最新评论

让您的驱动程序一次处理多个 I/O 请求

 
阅读更多

让您的驱动程序一次处理多个 I/O 请求

您的用户模式应用程序向您的驱动程序发送大量 I/O 请求,但是驱动程序坚持一次处理一个请求。问题是什么?

您可能认为驱动程序以一些模糊的方式发生阻塞或者应用程序中需要更多线程,但是解决方法经常简单得多:确保您的应用程序已经打开用于重叠 I/O 的设备。否则,I/O 管理器通过同步(使用文件对象中的锁)在调度 IRP 之前将 I/O 请求序列化。即使您的应用程序使用多个线程,一次(每个文件句柄)也只有一个请求可以通过。

在应用程序中启用重叠 I/O
要在应用程序中启用重叠 I/O,请将 dwFlagsAndAttributes 设置为 FILE_FLAG_OVERLAPPED(当您调用 CreateFile 来打开设备时)。(如果您跳过这个步骤,那么您不会得到重叠 I/O,即使其他方面正确无误。)

CreateFile 返回一个可以用来访问设备的句柄。当您使用 CreateFile 创建的文件句柄来调用 ReadFileWriteFileDeviceIoControl 时,提供一个 OVERLAPPED 结构(包含一个事件对象的句柄,该事件对象在操作完成时发出)的指针。对于重叠操作,这些函数会立即返回;操作完成时发出事件对象。

确保在函数调用中使用 OVERLAPPED 结构之前将其初始化为零,并且只设置函数需要的成员。例如,应该为 ReadFileWriteFile 调用设置 Offset 成员,但是对于 DeviceIoControl,这个成员应该是零。而且,确保为每个请求使用单独的 OVERLAPPED 结构;在前面的异步操作完成之前重用这个结构会导致错误。此外,您的应用程序将需要原始结构为请求调用 HasOverlappedIoCompletedGetOverlappedResult

打开用于重叠 I/O 的设备之后,可以简单地忽略 OVERLAPPED 结构来获得同步 I/O 吗?不可以,您必须为使用该句柄的所有函数调用(读、写或设备控制)提供一个 OVERLAPPED 结构。传递 NULL 会导致未定义的行为,即使驱动程序同步地完成请求。对于同步 I/O,只要应用程序等待 I/O 在返回之前完成,那么就可以在堆栈上声明 OVERLAPPED 结构。

下列代码片断显示如何打开一个用于写重叠 I/O 的文件:

  1. #include<windows.h>
  2. #include<stdio.h>
  3. HANDLEhFile;
  4. hFile=CreateFile(TEXT("myfile.txt"),//filetocreate
  5. GENERIC_WRITE,//openforwriting
  6. 0,//donotshare
  7. NULL,//defaultsecurity
  8. CREATE_ALWAYS,//overwriteexisting
  9. FILE_ATTRIBUTE_NORMAL|//normalfile
  10. FILE_FLAG_OVERLAPPED,//asynchronousI/O
  11. NULL);//noattr.template
  12. if(hFile==INVALID_HANDLE_VALUE)
  13. {
  14. printf("Couldnotopenfile(error%d)/n",GetLastError());
  15. return0;
  16. }

下列代码片断设置 OVERLAPPED 结构,调用 ReadFile,然后检查 I/O 请求的状态:

  1. OVERLAPPEDgOverlapped;
  2. //setupoverlappedstructurefields
  3. gOverLapped.Offset=0;
  4. gOverLapped.OffsetHigh=0;
  5. gOverLapped.hEvent=hEvent;
  6. //verifythatsizeof(inBuffer>=nBytestoRead)
  7. //attemptanasynchronousreadoperation
  8. bResult=ReadFile(hFile,&inBuffer,nBytesToRead,&nBytesRead,
  9. &gOverlapped);
  10. //iftherewasaproblem,ortheasync.operation'sstillpending...
  11. if(!bResult)
  12. {
  13. //dealwiththeerrorcode
  14. switch(dwError=GetLastError())
  15. {
  16. caseERROR_HANDLE_EOF:
  17. {
  18. //wehavereachedtheendofthefile
  19. //duringthecalltoReadFile
  20. //codetohandlethat
  21. }
  22. caseERROR_IO_PENDING:
  23. {
  24. //asynchronousi/oisstillinprogress
  25. //dosomethingelseforawhile
  26. GoDoSomethingElse();
  27. //checkontheresultsoftheasynchronousread
  28. bResult=GetOverlappedResult(hFile,&gOverlapped,
  29. &nBytesRead,FALSE);
  30. //iftherewasaproblem...
  31. if(!bResult)
  32. {
  33. //dealwiththeerrorcode
  34. switch(dwError=GetLastError())
  35. {
  36. caseERROR_HANDLE_EOF:
  37. {
  38. //wehavereachedtheendof
  39. //thefileduringasynchronous
  40. //operation
  41. }
  42. //dealwithothererrorcases
  43. }//endswitch(dwError=GetLastError())
  44. }
  45. }//endcase
  46. //dealwithothererrorcases,suchasthedefault
  47. }//endswitch(dwError=GetLastError())
  48. }//endif

在驱动程序中处理重叠 I/O
从驱动程序的角度来看,所有 I/O 请求都应该被视为异步。驱动程序不需要检查 I/O 请求是否真的异步,它应该简单地假设 I/O 请求异步并避免阻塞任何 I/O 请求。(驱动程序可以出于诸如获取锁等其他原因而阻塞,但是它不应该只是基于接收 I/O 请求而阻塞。)如果应用程序需要使用同步 I/O,那么它简单地打开设备而不指定重叠 I/O;然后 I/O 管理器如前所述将请求序列化,驱动程序方面无需任何特殊操作。

当 I/O 管理器从应用程序接收 I/O 请求时,I/O 管理器创建一个 IRP 并调用您的驱动程序的调度例程。调度程序的角色是将请求发给驱动程序进行处理,不必自己处理所有过程。如果您的驱动程序不在调度例程中完成 IRP,那么调用 IoMarkIrpPending 并返回 STATUS_PENDING。(请记住,如果您通过调用 IoMarkIrpPending 将 IRP 标记为挂起,那么可以返回的唯一的状态码是 STATUS_PENDING。不得返回任何其他状态码。但是,您可以将 IRP 标记为挂起,同步完成它,并返回 STATUS_PENDING。)

例如,考虑应用程序发送的 ReadFile 请求。在从 I/O 管理器接收到 IRP_MJ_READ IRP 时,您的调度例程可以简单地将 IRP 标记为挂起,调用 IoStartPacket,然后返回 STATUS_PENDING。(这里顺序很重要。如果您首先将请求放到队列中,那么它可以在第一个线程考虑将其标记为挂起之前被驱动程序的另一部分使用、处理、完成并释放。这将干扰 I/O 系统,并且还将在您试图在已经释放的内存池块的指针上调用 IoMarkIrpPending 时导致系统崩溃。

您的 StartIo 例程通过发送命令到控制器来开始操作。当控制器完成命令时,您的中断服务例程 (ISR) 被发出。它运行并排队一个延迟的过程调用 (DpcForIsr)。DpcForIsr 将合适的状态值放到 IRP 中并调用 IoCompleteRequest 来真正完成操作。DpcForIsr 然后调用位于处理 DpcForIsr 的线程上下文中的 IoStartNextPacket(在这里您的 StartIo 例程将被再次调用)来启动另一个请求。

同时,一旦您的驱动程序的调度例程返回,控制就返回到应用程序,因此应用程序不会在等待驱动程序完成 I/O 请求时发生阻塞。来自 ReadFile 的状态指示请求正在进行中(ReadFile 返回零,GetLastError 返回 ERROR_IO_PENDING),因此应用程序可以继续进行其他工作(可能发送更多 I/O 请求)。您的驱动程序调用 IoCompleteRequest 之后,应用程序被通知操作已经完成(通过 OVERLAPPED 结构中指定的事件对象),它可以检查该结构以获取状态信息。

最后一点:如果您挂起 IRP,那么您应该支持 I/O 取消,从而调用方可以取消 I/O 请求(如果它将耗时过长)。最好使用可以安全取消的 IRP 排队例程(IoCsqXxx 例程)(特别是如果您的驱动程序频繁执行 I/O),因为这些例程提供一个正确处理取消的框架从而使得竞争条件不会发生。Windows DDK(%winddk%/src/general/cancel)中的 Cancel 例子展示了这些例程的使用:

否则,您将需要在您的驱动程序中实现一个 Cancel 例程并在调用 IoStartPacket 时给该例程传递一个指针,因此 I/O 管理器将在 IRP 处于可取消状态时调用您的 StartIo 例程。在您的 StartIo 例程中,您将需要检查取消并防止相关的竞态条件。这种方法不推荐用于频繁执行 I/O 的驱动程序,因为 I/O 管理器在从设备队列插入和删除 IRP 时持有全局取消自旋锁,这会影响系统性能。

请参阅WHDC 上的“Windows 驱动程序中的取消逻辑” 以获取这些和其他 IRP 取消技巧的详细讨论。

您应该做什么?

在您的应用程序中:

  • 当调用 CreateFile 来打开设备时,将 dwFlagsAndAttributes 设置为 FILE_FLAG_OVERLAPPED。

  • 当调用 ReadFileWriteFileDeviceIoControl 时,提供一个正确初始化的 OVERLAPPED 结构的指针。使用以 FILE_FLAG_OVERLAPPED 打开的设备的句柄时,决不要忽略 OVERLAPPED 结构。

  • 决不要为随后的请求重用 OVERLAPPED 结构。

在您的驱动程序中:

  • 假设所有传入的 I/O 请求都是异步的。

  • 除非您在调度例程中完成 IRP,否则应该将 IRP 标记为挂起,从而应用程序不会被阻塞来等待请求完成。如果您将 IRP 标记为挂起,请记住只返回 STATUS_PENDING。

  • 支持 IRP 取消,首选使用可以安全取消的 IRP 排队例程 (IoCsqXxx),从而使得应用程序可以取消花费时间过长的 I/O 请求。

更多信息:

Platform SDK:存储
创建和打开文件
同步和重叠的输入输出

WHDC
处理 IRP:每个驱动程序编写人员都需要知道的技巧
Windows 驱动程序中的取消逻辑
可以安全取消的 IRP 排队的控制流程

Windows Driver Kit (WDK):内核模式驱动程序体系结构
处理 IRP

分享到:
评论

相关推荐

    后端开发 技术关键词: Node.js与Express框架 内容关键词: 事件驱动的非阻塞I/O

    计算机二级c语言真题后端开发 技术关键词: Node.js与Express框架 内容关键词: 事件驱动的非阻塞I/O ...Node.js的非阻塞I/O和事件驱动模型,使其非常适合处理高并发的实时应用,如在线聊天、实时数据流等。

    网络驱动程序设计指南

    第一部分 网络驱动程序 2 第一章 网络驱动程序设计指南的向导 3 第二章 内核模式驱动程序的网络结构 6 2.1 Windows 2000 网络结构和OSI模型 6 2.2 NDIS驱动程序 7 2.2.1 NDIS微端口驱动程序 7 2.2.2 NDIS中间层驱动...

    win2000驱动程序设计指南

    第1章 驱动程序开发环境 第2章 测试驱动程序 第一部分 一般内核模式 第1章Windows 2000和WDM驱动程序 第2章 分层的I/O、IRP和I/O对象 第3章 系统定义的对象和对驱动程序的支持 第4章 驱动程序...

    内核IO处理过程

    阶段不同,取决于I/O请求被定义通过单层的驱动程序操作设备,还是通过多层的驱动程序。处理过程不同,更多是取决于调用者指定的是同步还是异步I/O。因此,先讨论这两种I/O类型,然后再讲述其他类型。

    windows驱动开发技术详解-part2

     本章重点介绍了驱动程序中的处理IRP请求的派遣函数。所有对设备的操作最终将转化为IRP请求, 这些IRP请求会被传送到派遣函数处理。  7.1 IRP与派遣函数  7.1.1 IRP  7.1.2 IRP类型  7.1.3 对派遣函数的...

    字符设备驱动.doc

    Linux对于I/O请求有对应的缓冲区,可以选择响应顺序 块设备可以被随机访问 字符设备驱动程序开发流程 设备号 字符设备驱动的重要数据结构介绍 字符设备的注册流程 字符设备相关操作 创建设备文件 编写驱动程序...

    操作系统精髓与设计原理答案

    1.6、考虑一个计算机系统,它包含一个I/O模块,用以控制一台简单的键盘/打印机电传打字设备。CPU中包含下列寄存器,这些寄存器直接连接到系统总线上: INPR:输入寄存器,8位 OUTR:输出寄存器,8位 FGI:输入标记,...

    Windows驱动开发技术详解的光盘-part1

    本书共分23章,内容涵盖了Windows操作系统的基本原理、NT驱动程序与WDM驱动程序的构造、驱动程序中的同步异步处理方法、驱动程序中即插即用功能、驱动程序的各种调试技巧等。同时,还针对流行的PCI驱动程序、USB驱动...

    \Windows网络编程技术

    找到设备后, I / O请求会被转发给一个本地设备驱动程序。通过网络来访问一个设备也同样。然而, I / O请求必须通过网络转发给对应的远程设备。我们将其称为“ I / O重定向”(I/O Redirection)。例如,Wi n d o w ...

    磁盘调度 C++的

    当有多个进程提出输入输出请求而处于等待状态时,可用电梯调度算法从若干个等待访问者中选择一个进程,让它访问磁盘。为此设置“驱动调度”进程。 3、 由于磁盘与处理器是并行工作的,所以当磁盘在为一个进程服务时...

    操作系统简答题试题及答案.doc

    1. I/O软件一般分为哪几个层次? 从硬件层到用户层分为中断处理程序;设备驱动程序;与设备无关的I/O软件;用户 空间的I/O软件等4层。 2. 操作系统有哪些基本类型? 基本的操作系统类型有三种:多道批处理操作系统,...

    在C++编程领域,有一段备受推崇的代码-“boost::asio”,它被认为是世界上最强大的C++代码之一

    它基于事件驱动的模型,允许开发者同时处理多个连接和请求,并且不会阻塞主线程。boost::asio库还提供了可靠的数据传输机制、安全的加密方式和灵活的配置选项,使得开发者可以轻松地构建各种类型的网络应用程序,...

    Linux DeviceDrivers 3rd Edition

    (英文版 第三版 中文书签我自己做的 大家要是不喜欢就用福听PDF删除掉我加上去的东西 我还有很多好资源 大家都去逛逛 还有这个是单本的 网上还有个分很多章的) 前言 1 第一章 设备驱动程序简介 9 设备驱动程序...

    适用于 Apache Cassandra 的 DataStax C/C++驱动程序_C++_代码_相关文件_下载

    异步 I/O、并行执行和请求流水线 连接池 自动节点发现 自动重新连接 可配置的负载均衡 适用于任何集群大小 验证 SSL 延迟感知路由 性能指标 元组和UDT 嵌套集合 重试策略 客户端时间戳 数据类型 空闲连接心跳 支持...

    编写设备驱动程序

    介绍了一种常用的驱动程序编写方法,该方法允许在编写驱动程序时忽略特定于平台的问题,如字节存储顺序(endianness)和数据排序等。 其他主题包括:强化Solaris驱动程序;电源管理;驱动程序自动配置;程控I/O;直接...

    DELPHI串口编程

    但需要为每个I/O设备分配一个中断请求号 和相应的中断服务程序,此外还需要一个中断控制器(I/O接口芯 片)管理I/O设备提出的中断请求,例如设置中断屏蔽、中断请求 优先级等。此外,中断处理方式的缺点是每传送...

    深入解析Windows操作系统中文.part2.rar

    针对分层的驱动程序的I/O请求 577 I/O完成端口 585 驱动程序检验器(Driver Verifier) 589 9.4 即插即用(PnP)管理器 590 即插即用支持的级别 591 驱动程序对于即插即用的支持 592 驱动程序加载、初始化和安装 594...

    linux设备驱动程序第三版

    驱动程序的角色 ......................................................................................................... 17 2.2 1.2. 划分内核 ............................................................

    FreeBSD操作系统设计与实现

    7.3.2 I/O请求通过CAM子系统的路径 7.4 ATA层 7.5 配置设备 7.5.1 识别设备 7.5.2 自动配置数据结构 7.5.3 资源管理 7.6 复习题 7.7 参考文献 第8章 本地文件系统 8.1 文件系统的分层管理 8.2 inode的结构 8.2.1 ...

    jsr80 java 访问 usb

    具有多个接口的设备可以同时被多个应用程序(或者设备驱动程序)所访问,其中每一个应用程序(或者设备驱动程序)都占据一个不同的接口。该 API 支持控制传输、批量传输和中断传输,不支持等时传输,因为等时传输用于...

Global site tag (gtag.js) - Google Analytics