一 废话

  在上一篇文章中,我们介绍了通过等待内核对象来接受I/O完成通知的重叠I/O。除了使用同步对象外,我们还可以使用其它方法,这便是这篇文章要介绍的使用完成例程的扩展I/O。完成例程其实就是回调函数,当I/O完成的时候系统调用一个用户指定的回调函数来通知用户I/O完成, 调用完回调函数之后,可以继续启动下一个I/O操作。为了实现回调,线程需要处于可通知的状态。为什么称之为“扩展I/O”呢?因为它是等待内核对象的异步I/O的扩展,而且它需要调用扩展函数。

二 相关数据结构和函数

  1 ReadFileEx 和 WriteFileEx

  为什么是ReadFileEx和WriteFileEx,而不是使用函数ReadFile和WriteFile?额。。。一是因为当I/O完成的时候需要调用用户设置的回调函数,而回调函数的地址怎么和相关I/O异步过程调用队列相关联;二是ReadFile和WriteFile发送I/O操作请求时,异步情况下会立刻返回,所以函数参数中的已传输字节数这个参数是没有用的。所以,我们需要新的函数ReadFileEx和WriteFileEx,下面是两个函数的原型:

  1. BOOL
  2. WINAPI
  3. ReadFileEx(
  4. HANDLE hFile,
  5. (FILE) LPVOID lpBuffer,
  6. DWORD nNumberOfBytesToRead,
  7. LPOVERLAPPED lpOverlapped,
  8. LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
  9. );
  10.  
  11. BOOL
  12. WINAPI
  13. WriteFileEx(
  14. HANDLE hFile,
  15. (nNumberOfBytesToWrite) LPCVOID lpBuffer,
  16. DWORD nNumberOfBytesToWrite,
  17. LPOVERLAPPED lpOverlapped,
  18. LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
  19. );

  ReadFileEx和WriteFileEx的前三个参数和ReadFile和WriteFile中的一样。lpOverlapped必须提供提供OVERLAPPED结构,但是不用设置hEvent成员,系统将忽略它。但是,可以将其设置为表示I/O操作的信息,比如顺序号。lpCompletionRoutine是所要设置的I/O回调函数的地址,回调函数的原型为:

  1. VOID
  2. (WINAPI *LPOVERLAPPED_COMPLETION_ROUTINE)(
  3. __in DWORD dwErrorCode,
  4. __in DWORD dwNumberOfBytesTransfered,
  5. __inout LPOVERLAPPED lpOverlapped
  6. );

  

  2 可提醒的等待函数

  当I/O请求完成时候,系统会将它们添加到线程的APC队列中——回调函数并不会立即被调用,这是因为线程可能还在忙于其它的事情。为了对线程APC队列中的项进行处理,线程必须将自己设置为可提醒状态。这样当线程执行到可提醒状态点时,而APC队列中刚好有已经完成的I/O操作,则会调用回到函数。Windws共提供了6个函数可将线程置为可提醒状态:

  1. WINBASEAPI
  2. DWORD
  3. WINAPI
  4. SleepEx(
  5. __in DWORD dwMilliseconds,
  6. __in BOOL bAlertable
  7. );
  8.  
  9. WINBASEAPI
  10. DWORD
  11. WINAPI
  12. WaitForSingleObjectEx(
  13. __in HANDLE hHandle,
  14. __in DWORD dwMilliseconds,
  15. __in BOOL bAlertable
  16. );
  17.  
  18. WINBASEAPI
  19. DWORD
  20. WINAPI
  21. WaitForMultipleObjectsEx(
  22. __in DWORD nCount,
  23. __in_ecount(nCount) CONST HANDLE *lpHandles,
  24. __in BOOL bWaitAll,
  25. __in DWORD dwMilliseconds,
  26. __in BOOL bAlertable
  27. );
  28.  
  29. WINBASEAPI
  30. DWORD
  31. WINAPI
  32. SignalObjectAndWait(
  33. __in HANDLE hObjectToSignal,
  34. __in HANDLE hObjectToWaitOn,
  35. __in DWORD dwMilliseconds,
  36. __in BOOL bAlertable
  37. );
  38.  
  39. BOOL
  40. WINAPI
  41. GetQueuedCompletionStatusEx(
  42. __in HANDLE CompletionPort,
  43. __out_ecount_part(ulCount, *ulNumEntriesRemoved) LPOVERLAPPED_ENTRY lpCompletionPortEntries,
  44. __in ULONG ulCount,
  45. __out PULONG ulNumEntriesRemoved,
  46. __in DWORD dwMilliseconds,
  47. __in BOOL fAlertable
  48. );
  49.  
  50. DWORD
  51. WINAPI
  52. MsgWaitForMultipleObjectsEx(
  53. __in DWORD nCount,
  54. __in_ecount_opt(nCount) CONST HANDLE *pHandles,
  55. __in DWORD dwMilliseconds,
  56. __in DWORD dwWakeMask,
  57. __in DWORD dwFlags);  

  前五个函数的最后一个参数是一个布尔值,表示调用线程是否应该将自己置为可提醒状态。最后一个函数MsgWaitForMutipleObjectsEx需要使用MWMO_ALTERABLE来让线程进入可提醒状态。返回值表示它们返回的原因,如果返回的或者通过GetLastError为WAIT_IO_COMPLETION,表示线程至少处理了APC队列中的一项。

  注意:

  • 对任何可提醒的等待函数使用INFINITE超时值。
  • 使用重叠结构中的hEvent数据成员来将信息传递给回调函数。

  3  异步过程调用队列(asynchronous procedure call, APC)

  到底完成例程的I/O操作是怎么运转的呢?我们从头开始梳理。这就需要了解异步过程调用队列(asynchronous procedure call, APC),APC队列是由系统在内部维护的。当系统创建一个线程的时候,会同是创建一个与之相关联的队列,称之为异步过程调用。当我们调用ReadFileEx或WriteFileEx向设备驱动程序发出一个I/O请求后立刻返回,但是会将回调函数的地址传给设备驱动程序。当设备驱动程序完成I/O请求的时候,便会在发出I/O请求的线程的APC队列中添加一项。该项包含了完成函数的地址,以及发出此I/O请求所使用的OVERLAPPED结构的地址。

  当我们调用可提醒函数将线程设置为可提醒状态时,系统会首先检查线程的APC队列。如果队列中至少有一项,系统便会将APC队列中的那一项取出,让线程调用回调函数,并在OVERLAPPED结构中传入已完成I/O请求的错误码,已传输的字节数,以及OVERLAPPED结构的地址。当回调函数返回的时候,系统会检查APC队列是否还有其它的项,如果还有则继续处理下一项。即当一个线程进入可提醒状态时,该线程的APC队列中的所有完成例程都会得到执行。注意,系统会以任意的顺序执行我们添加到队列中的I/O请求。

三 示例

重叠I/O之使用完成例程的扩展I/O【系列二】的更多相关文章

  1. zw版【转发·台湾nvp系列例程】halcon与delphi系列例程

    zw版[转发·台湾nvp系列例程]halcon与delphi系列例程 台湾nvp技术论坛,是目前halcon与delphi例程最多的网站,也是唯一成系列的, http://zip.nvp.com.tw ...

  2. 重叠I/O模型

    一. 重叠I/O的概念当调用ReadFile和WriteFile时,如果最后一个参数lpOverlapped设置为NULL,那么线程就阻塞在这里,直到读写完指定的数据后,它们才返回.这样在读写大文件的 ...

  3. 套接字I/O模型-重叠I/O

    重叠模型的基本设计原理是让应用程序使用重叠的数据结构,一次投递一个或多个WinsockI/O请求.针对那些提交的请求,在它们完成之后,应用程序可为它们提供服务.模型的总体设计以Windows重叠I/O ...

  4. 四.Windows I/O模型之重叠IO(overlapped)模型

    1.适用于除Windows CE之外的各种Windows平台.在使用这个模型之前应该确保该系统安装了Winsock2.重叠模型的基本设计原理是使用一个重叠的数据结构,一次投递一个或多个Winsock ...

  5. IOCP入门

    完成端口(Completion Port)详解 此文讲解最好,也很全面一下其他文章看看就行,也可不看. 单句柄数据,单IO数据 此文讲述比较清晰,可以辅助理解上文. IOCP编程之基本原理:http: ...

  6. 完成端口(Completion Port)详解(转)

    手把手叫你玩转网络编程系列之三    完成端口(Completion Port)详解                                                           ...

  7. 完成端口(CompletionPort)详解

    手把手叫你玩转网络编程系列之三    完成端口(Completion Port)详解                                                           ...

  8. 完毕port(CompletionPort)具体解释 - 手把手教你玩转网络编程系列之三

       手把手叫你玩转网络编程系列之三    完毕port(Completion Port)具体解释                                                    ...

  9. 完成端口IOCP详解

    修改自: http://blog.csdn.net/piggyxp/article/details/6922277 ps: 原作者很厉害了, 把一个iocp模型讲解的这么形象,不过在实践过程中发现一些 ...

随机推荐

  1. HDU 5710 Digit-Sum (构造)

    题意: 定义S(N) 为数字N每个位上数字的和.在给两个数a,b,求最小的正整数n,使得 a×S(n)=b×S(2n). 官方题解: 这道题目的结果可能非常大,所以我们直接枚举n是要GG的. 首先可以 ...

  2. FreeModbus Slave 改进的eMbPoll()【worldsing 笔记】

    eMbPoll()的作用是FreeMod协议通信过程中不断查询事件对列有无完速数据桢,并进行地址和CRD验证,最后运行和回复主机. 为了减小代码尺寸对eMbPoll进行改进: 原版: 1:  2: e ...

  3. 【P3O是什么】P3O认证之项目组合、项目群和项目办公室

    如何区分项目组合办公室与项目群和项目办公室? P3O®(Portfolio,Programmed and Project Offices-项目组合.项目群和项目办公室)是由英国商务部 OGC 于200 ...

  4. http 需要掌握的知识点(一)

    超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议.HTTP 也属于 TCP/IP 协议族的子集,想要学习 HTTP ,先需要了解 ...

  5. Delphi- ini文件的读写操作

    一.读INI文件示例 procedure TForm1.FormCreate(Sender: TObject); Var MyIni :Tinifile; glAppPath :string; beg ...

  6. 【C++深入浅出】智能指针之auto_ptr学习

    起:  C++98标准加入auto_ptr,即智能指针,C++11加入shared_ptr和weak_ptr两种智能指针,先从auto_ptr的定义学习一下auto_ptr的用法. template& ...

  7. 软件测试之WEB测试经典总结

    在Web工程过程中,基于Web系统的测试.确认和验收是一项重要而富有挑战性的工作.基于Web的系统测试与传统的软件测试不同,它不但需要检查和验证是否按照设计的要求运行,而且还要测试系统在不同用户的浏览 ...

  8. Java连接MYSQL【转载】

    这篇文章主要以MySQL为例讲下Java如何连接到数据库的. 当然,首先要安装有JDK(一般是JDK1.5.X).然后安装MySQL,这些都比较简单,具体过程就不说了.配置好这两个环境后,下载JDBC ...

  9. android自动打包方法(ant+proguard+签名)

    前段时间做了一个android的网游项目,现在优化减少体积和防止别人反编译,需要把编译后.class进行混淆,开始在网上看了一些关于 ProGuard的介绍,基本上都是使用ADT自带的打包方式,那个打 ...

  10. (转) 如何在JavaScript与ActiveX之间传递数据1

    本文研究如何在JS等脚本语言与ActiveX控件之间通信,如何传递各种类型的参数,以及COM的IDispatch接口.使用类似的方法,可以推广到其他所有脚本型语言,如LUA,AutoCad等.本文将研 ...